Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,432 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T>
|
||||
struct AtomicIntegerStorage;
|
||||
|
||||
template<typename T> requires (sizeof(T) == sizeof(u8))
|
||||
struct AtomicIntegerStorage<T> {
|
||||
using Type = u8;
|
||||
};
|
||||
|
||||
template<typename T> requires (sizeof(T) == sizeof(u16))
|
||||
struct AtomicIntegerStorage<T> {
|
||||
using Type = u16;
|
||||
};
|
||||
|
||||
template<typename T> requires (sizeof(T) == sizeof(u32))
|
||||
struct AtomicIntegerStorage<T> {
|
||||
using Type = u32;
|
||||
};
|
||||
|
||||
template<typename T> requires (sizeof(T) == sizeof(u64))
|
||||
struct AtomicIntegerStorage<T> {
|
||||
using Type = u64;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept UsableAtomicType = (sizeof(T) <= sizeof(u64)) && !std::is_const<T>::value && !std::is_volatile<T>::value && (std::is_pointer<T>::value || requires (const T &t) {
|
||||
std::bit_cast<typename AtomicIntegerStorage<T>::Type, T>(t);
|
||||
});
|
||||
|
||||
template<UsableAtomicType T>
|
||||
using AtomicStorage = typename AtomicIntegerStorage<T>::Type;
|
||||
|
||||
|
||||
static_assert(std::same_as<AtomicStorage<void *>, u64>);
|
||||
|
||||
static_assert(std::same_as<AtomicStorage<s8>, u8>);
|
||||
static_assert(std::same_as<AtomicStorage<u8>, u8>);
|
||||
static_assert(std::same_as<AtomicStorage<s16>, u16>);
|
||||
static_assert(std::same_as<AtomicStorage<u16>, u16>);
|
||||
static_assert(std::same_as<AtomicStorage<s32>, u32>);
|
||||
static_assert(std::same_as<AtomicStorage<u32>, u32>);
|
||||
static_assert(std::same_as<AtomicStorage<s64>, u64>);
|
||||
static_assert(std::same_as<AtomicStorage<u64>, u64>);
|
||||
|
||||
ALWAYS_INLINE void ClearExclusiveForAtomic() {
|
||||
__asm__ __volatile__("clrex" ::: "memory");
|
||||
}
|
||||
|
||||
#define AMS_UTIL_IMPL_DEFINE_ATOMIC_LOAD_FUNCTION(_FNAME_, _MNEMONIC_) \
|
||||
template<std::unsigned_integral T> T _FNAME_ ##ForAtomic(const volatile T *); \
|
||||
\
|
||||
template<> ALWAYS_INLINE u8 _FNAME_ ##ForAtomic(const volatile u8 *p) { u8 v; __asm__ __volatile__(_MNEMONIC_ "b %w[v], %[p]" : [v]"=r"(v) : [p]"Q"(*p) : "memory"); return v; } \
|
||||
template<> ALWAYS_INLINE u16 _FNAME_ ##ForAtomic(const volatile u16 *p) { u16 v; __asm__ __volatile__(_MNEMONIC_ "h %w[v], %[p]" : [v]"=r"(v) : [p]"Q"(*p) : "memory"); return v; } \
|
||||
template<> ALWAYS_INLINE u32 _FNAME_ ##ForAtomic(const volatile u32 *p) { u32 v; __asm__ __volatile__(_MNEMONIC_ " %w[v], %[p]" : [v]"=r"(v) : [p]"Q"(*p) : "memory"); return v; } \
|
||||
template<> ALWAYS_INLINE u64 _FNAME_ ##ForAtomic(const volatile u64 *p) { u64 v; __asm__ __volatile__(_MNEMONIC_ " %[v], %[p]" : [v]"=r"(v) : [p]"Q"(*p) : "memory"); return v; }
|
||||
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_LOAD_FUNCTION(LoadAcquire, "ldar")
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_LOAD_FUNCTION(LoadExclusive, "ldxr")
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_LOAD_FUNCTION(LoadAcquireExclusive, "ldaxr")
|
||||
|
||||
#undef AMS_UTIL_IMPL_DEFINE_ATOMIC_LOAD_FUNCTION
|
||||
|
||||
template<std::unsigned_integral T> void StoreReleaseForAtomic(volatile T *, T);
|
||||
|
||||
template<> ALWAYS_INLINE void StoreReleaseForAtomic(volatile u8 *p, u8 v) { __asm__ __volatile__("stlrb %w[v], %[p]" : : [v]"r"(v), [p]"Q"(*p) : "memory"); }
|
||||
template<> ALWAYS_INLINE void StoreReleaseForAtomic(volatile u16 *p, u16 v) { __asm__ __volatile__("stlrh %w[v], %[p]" : : [v]"r"(v), [p]"Q"(*p) : "memory"); }
|
||||
template<> ALWAYS_INLINE void StoreReleaseForAtomic(volatile u32 *p, u32 v) { __asm__ __volatile__("stlr %w[v], %[p]" : : [v]"r"(v), [p]"Q"(*p) : "memory"); }
|
||||
template<> ALWAYS_INLINE void StoreReleaseForAtomic(volatile u64 *p, u64 v) { __asm__ __volatile__("stlr %[v], %[p]" : : [v]"r"(v), [p]"Q"(*p) : "memory"); }
|
||||
|
||||
#define AMS_UTIL_IMPL_DEFINE_ATOMIC_STORE_EXCLUSIVE_FUNCTION(_FNAME_, _MNEMONIC_) \
|
||||
template<std::unsigned_integral T> bool _FNAME_ ##ForAtomic(volatile T *, T); \
|
||||
\
|
||||
template<> ALWAYS_INLINE bool _FNAME_ ##ForAtomic(volatile u8 *p, u8 v) { int result; __asm__ __volatile__(_MNEMONIC_ "b %w[result], %w[v], %[p]" : [result]"=&r"(result) : [v]"r"(v), [p]"Q"(*p) : "memory"); return result == 0; } \
|
||||
template<> ALWAYS_INLINE bool _FNAME_ ##ForAtomic(volatile u16 *p, u16 v) { int result; __asm__ __volatile__(_MNEMONIC_ "h %w[result], %w[v], %[p]" : [result]"=&r"(result) : [v]"r"(v), [p]"Q"(*p) : "memory"); return result == 0; } \
|
||||
template<> ALWAYS_INLINE bool _FNAME_ ##ForAtomic(volatile u32 *p, u32 v) { int result; __asm__ __volatile__(_MNEMONIC_ " %w[result], %w[v], %[p]" : [result]"=&r"(result) : [v]"r"(v), [p]"Q"(*p) : "memory"); return result == 0; } \
|
||||
template<> ALWAYS_INLINE bool _FNAME_ ##ForAtomic(volatile u64 *p, u64 v) { int result; __asm__ __volatile__(_MNEMONIC_ " %w[result], %[v], %[p]" : [result]"=&r"(result) : [v]"r"(v), [p]"Q"(*p) : "memory"); return result == 0; }
|
||||
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_STORE_EXCLUSIVE_FUNCTION(StoreExclusive, "stxr")
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_STORE_EXCLUSIVE_FUNCTION(StoreReleaseExclusive, "stlxr")
|
||||
|
||||
#undef AMS_UTIL_IMPL_DEFINE_ATOMIC_STORE_EXCLUSIVE_FUNCTION
|
||||
|
||||
template<UsableAtomicType T>
|
||||
constexpr ALWAYS_INLINE T ConvertToTypeForAtomic(AtomicStorage<T> s) {
|
||||
if constexpr (std::integral<T>) {
|
||||
return static_cast<T>(s);
|
||||
} else if constexpr(std::is_pointer<T>::value) {
|
||||
return reinterpret_cast<T>(s);
|
||||
} else {
|
||||
return std::bit_cast<T>(s);
|
||||
}
|
||||
}
|
||||
|
||||
template<UsableAtomicType T>
|
||||
constexpr ALWAYS_INLINE AtomicStorage<T> ConvertToStorageForAtomic(T arg) {
|
||||
if constexpr (std::integral<T>) {
|
||||
return static_cast<AtomicStorage<T>>(arg);
|
||||
} else if constexpr(std::is_pointer<T>::value) {
|
||||
if (std::is_constant_evaluated() && arg == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return reinterpret_cast<AtomicStorage<T>>(arg);
|
||||
} else {
|
||||
return std::bit_cast<AtomicStorage<T>>(arg);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::memory_order Order, std::unsigned_integral StorageType>
|
||||
ALWAYS_INLINE StorageType AtomicLoadImpl(volatile StorageType * const p) {
|
||||
if constexpr (Order != std::memory_order_relaxed) {
|
||||
return ::ams::util::impl::LoadAcquireForAtomic(p);
|
||||
} else {
|
||||
return *p;
|
||||
}
|
||||
}
|
||||
|
||||
template<std::memory_order Order, std::unsigned_integral StorageType>
|
||||
ALWAYS_INLINE void AtomicStoreImpl(volatile StorageType * const p, const StorageType s) {
|
||||
if constexpr (Order != std::memory_order_relaxed) {
|
||||
::ams::util::impl::StoreReleaseForAtomic(p, s);
|
||||
} else {
|
||||
*p = s;
|
||||
}
|
||||
}
|
||||
|
||||
template<std::memory_order Order, std::unsigned_integral StorageType>
|
||||
ALWAYS_INLINE StorageType LoadExclusiveForAtomicByMemoryOrder(volatile StorageType * const p) {
|
||||
if constexpr (Order == std::memory_order_relaxed) {
|
||||
return ::ams::util::impl::LoadExclusiveForAtomic(p);
|
||||
} else if constexpr (Order == std::memory_order_consume || Order == std::memory_order_acquire) {
|
||||
return ::ams::util::impl::LoadAcquireExclusiveForAtomic(p);
|
||||
} else if constexpr (Order == std::memory_order_release) {
|
||||
return ::ams::util::impl::LoadExclusiveForAtomic(p);
|
||||
} else if constexpr (Order == std::memory_order_acq_rel || Order == std::memory_order_seq_cst) {
|
||||
return ::ams::util::impl::LoadAcquireExclusiveForAtomic(p);
|
||||
} else {
|
||||
static_assert(false, "Invalid memory order");
|
||||
}
|
||||
}
|
||||
|
||||
template<std::memory_order Order, std::unsigned_integral StorageType>
|
||||
ALWAYS_INLINE bool StoreExclusiveForAtomicByMemoryOrder(volatile StorageType * const p, const StorageType s) {
|
||||
if constexpr (Order == std::memory_order_relaxed) {
|
||||
return ::ams::util::impl::StoreExclusiveForAtomic(p, s);
|
||||
} else if constexpr (Order == std::memory_order_consume || Order == std::memory_order_acquire) {
|
||||
return ::ams::util::impl::StoreExclusiveForAtomic(p, s);
|
||||
} else if constexpr (Order == std::memory_order_release) {
|
||||
return ::ams::util::impl::StoreReleaseExclusiveForAtomic(p, s);
|
||||
} else if constexpr (Order == std::memory_order_acq_rel || Order == std::memory_order_seq_cst) {
|
||||
return ::ams::util::impl::StoreReleaseExclusiveForAtomic(p, s);
|
||||
} else {
|
||||
static_assert(false, "Invalid memory order");
|
||||
}
|
||||
}
|
||||
|
||||
template<std::memory_order Order, std::unsigned_integral StorageType>
|
||||
ALWAYS_INLINE StorageType AtomicExchangeImpl(volatile StorageType * const p, const StorageType s) {
|
||||
StorageType current;
|
||||
do {
|
||||
current = ::ams::util::impl::LoadExclusiveForAtomicByMemoryOrder<Order>(p);
|
||||
} while(AMS_UNLIKELY(!impl::StoreExclusiveForAtomicByMemoryOrder<Order>(p, s)));
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
template<std::memory_order Order, UsableAtomicType T>
|
||||
ALWAYS_INLINE bool AtomicCompareExchangeWeakImpl(volatile AtomicStorage<T> * const p, T &expected, T desired) {
|
||||
const AtomicStorage<T> e = ::ams::util::impl::ConvertToStorageForAtomic(expected);
|
||||
const AtomicStorage<T> d = ::ams::util::impl::ConvertToStorageForAtomic(desired);
|
||||
|
||||
const AtomicStorage<T> current = ::ams::util::impl::LoadExclusiveForAtomicByMemoryOrder<Order>(p);
|
||||
if (AMS_UNLIKELY(current != e)) {
|
||||
impl::ClearExclusiveForAtomic();
|
||||
expected = ::ams::util::impl::ConvertToTypeForAtomic<T>(current);
|
||||
return false;
|
||||
}
|
||||
|
||||
return AMS_LIKELY(impl::StoreExclusiveForAtomicByMemoryOrder<Order>(p, d));
|
||||
}
|
||||
|
||||
template<std::memory_order Order, UsableAtomicType T>
|
||||
ALWAYS_INLINE bool AtomicCompareExchangeStrongImpl(volatile AtomicStorage<T> * const p, T &expected, T desired) {
|
||||
const AtomicStorage<T> e = ::ams::util::impl::ConvertToStorageForAtomic(expected);
|
||||
const AtomicStorage<T> d = ::ams::util::impl::ConvertToStorageForAtomic(desired);
|
||||
|
||||
do {
|
||||
if (const AtomicStorage<T> current = ::ams::util::impl::LoadExclusiveForAtomicByMemoryOrder<Order>(p); AMS_UNLIKELY(current != e)) {
|
||||
impl::ClearExclusiveForAtomic();
|
||||
expected = ::ams::util::impl::ConvertToTypeForAtomic<T>(current);
|
||||
return false;
|
||||
}
|
||||
} while (AMS_UNLIKELY(!impl::StoreExclusiveForAtomicByMemoryOrder<Order>(p, d)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<impl::UsableAtomicType T>
|
||||
class Atomic {
|
||||
NON_COPYABLE(Atomic);
|
||||
NON_MOVEABLE(Atomic);
|
||||
private:
|
||||
using StorageType = impl::AtomicStorage<T>;
|
||||
|
||||
static constexpr bool IsIntegral = std::integral<T>;
|
||||
static constexpr bool IsPointer = std::is_pointer<T>::value;
|
||||
|
||||
static constexpr bool HasArithmeticFunctions = IsIntegral || IsPointer;
|
||||
|
||||
using DifferenceType = typename std::conditional<IsIntegral, T, typename std::conditional<IsPointer, std::ptrdiff_t, void>::type>::type;
|
||||
|
||||
static constexpr ALWAYS_INLINE T ConvertToType(StorageType s) {
|
||||
return impl::ConvertToTypeForAtomic<T>(s);
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE StorageType ConvertToStorage(T arg) {
|
||||
return impl::ConvertToStorageForAtomic<T>(arg);
|
||||
}
|
||||
private:
|
||||
StorageType m_v;
|
||||
private:
|
||||
ALWAYS_INLINE volatile StorageType *GetStoragePointer() { return reinterpret_cast< volatile StorageType *>(std::addressof(m_v)); }
|
||||
ALWAYS_INLINE const volatile StorageType *GetStoragePointer() const { return reinterpret_cast<const volatile StorageType *>(std::addressof(m_v)); }
|
||||
public:
|
||||
ALWAYS_INLINE Atomic() { /* ... */ }
|
||||
constexpr ALWAYS_INLINE Atomic(T v) : m_v(ConvertToStorage(v)) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE T operator=(T desired) {
|
||||
if (std::is_constant_evaluated()) {
|
||||
m_v = ConvertToStorage(desired);
|
||||
} else {
|
||||
this->Store(desired);
|
||||
}
|
||||
return desired;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE operator T() const { return this->Load(); }
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE T Load() const {
|
||||
return ConvertToType(impl::AtomicLoadImpl<Order>(this->GetStoragePointer()));
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE void Store(T arg) {
|
||||
return impl::AtomicStoreImpl<Order>(this->GetStoragePointer(), ConvertToStorage(arg));
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE T Exchange(T arg) {
|
||||
return ConvertToType(impl::AtomicExchangeImpl<Order>(this->GetStoragePointer(), ConvertToStorage(arg)));
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE bool CompareExchangeWeak(T &expected, T desired) {
|
||||
return impl::AtomicCompareExchangeWeakImpl<Order, T>(this->GetStoragePointer(), expected, desired);
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE bool CompareExchangeStrong(T &expected, T desired) {
|
||||
return impl::AtomicCompareExchangeStrongImpl<Order, T>(this->GetStoragePointer(), expected, desired);
|
||||
}
|
||||
|
||||
#define AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(_OPERATION_, _OPERATOR_, _POINTER_ALLOWED_) \
|
||||
template<bool Enable = (IsIntegral || (_POINTER_ALLOWED_ && IsPointer)), typename = typename std::enable_if<Enable, void>::type> \
|
||||
ALWAYS_INLINE T Fetch ## _OPERATION_(DifferenceType arg) { \
|
||||
static_assert(Enable == (IsIntegral || (_POINTER_ALLOWED_ && IsPointer))); \
|
||||
volatile StorageType * const p = this->GetStoragePointer(); \
|
||||
\
|
||||
StorageType current; \
|
||||
do { \
|
||||
current = impl::LoadAcquireExclusiveForAtomic<StorageType>(p); \
|
||||
} while (AMS_UNLIKELY(!impl::StoreReleaseExclusiveForAtomic<StorageType>(p, ConvertToStorage(ConvertToType(current) _OPERATOR_ arg)))); \
|
||||
return ConvertToType(current); \
|
||||
} \
|
||||
\
|
||||
template<bool Enable = (IsIntegral || (_POINTER_ALLOWED_ && IsPointer)), typename = typename std::enable_if<Enable, void>::type> \
|
||||
ALWAYS_INLINE T operator _OPERATOR_##=(DifferenceType arg) { \
|
||||
static_assert(Enable == (IsIntegral || (_POINTER_ALLOWED_ && IsPointer))); \
|
||||
return this->Fetch ## _OPERATION_(arg) _OPERATOR_ arg; \
|
||||
}
|
||||
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Add, +, true)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Sub, -, true)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(And, &, false)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Or, |, false)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Xor, ^, false)
|
||||
|
||||
#undef AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator++() { static_assert(Enable == HasArithmeticFunctions); return this->FetchAdd(1) + 1; }
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator++(int) { static_assert(Enable == HasArithmeticFunctions); return this->FetchAdd(1); }
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator--() { static_assert(Enable == HasArithmeticFunctions); return this->FetchSub(1) - 1; }
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator--(int) { static_assert(Enable == HasArithmeticFunctions); return this->FetchSub(1); }
|
||||
};
|
||||
|
||||
template<impl::UsableAtomicType T>
|
||||
class AtomicRef {
|
||||
NON_MOVEABLE(AtomicRef);
|
||||
public:
|
||||
static constexpr size_t RequiredAlignment = std::max<size_t>(sizeof(T), alignof(T));
|
||||
private:
|
||||
using StorageType = impl::AtomicStorage<T>;
|
||||
static_assert(sizeof(StorageType) == sizeof(T));
|
||||
static_assert(alignof(StorageType) >= alignof(T));
|
||||
|
||||
static constexpr bool IsIntegral = std::integral<T>;
|
||||
static constexpr bool IsPointer = std::is_pointer<T>::value;
|
||||
|
||||
static constexpr bool HasArithmeticFunctions = IsIntegral || IsPointer;
|
||||
|
||||
using DifferenceType = typename std::conditional<IsIntegral, T, typename std::conditional<IsPointer, std::ptrdiff_t, void>::type>::type;
|
||||
|
||||
static constexpr ALWAYS_INLINE T ConvertToType(StorageType s) {
|
||||
return impl::ConvertToTypeForAtomic<T>(s);
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE StorageType ConvertToStorage(T arg) {
|
||||
return impl::ConvertToStorageForAtomic<T>(arg);
|
||||
}
|
||||
private:
|
||||
volatile StorageType * const m_p;
|
||||
private:
|
||||
ALWAYS_INLINE volatile StorageType *GetStoragePointer() const { return m_p; }
|
||||
public:
|
||||
explicit ALWAYS_INLINE AtomicRef(T &t) : m_p(reinterpret_cast<volatile StorageType *>(std::addressof(t))) { /* ... */ }
|
||||
ALWAYS_INLINE AtomicRef(const AtomicRef &) noexcept = default;
|
||||
|
||||
AtomicRef() = delete;
|
||||
AtomicRef &operator=(const AtomicRef &) = delete;
|
||||
|
||||
ALWAYS_INLINE T operator=(T desired) const { return const_cast<AtomicRef *>(this)->Store(desired); }
|
||||
|
||||
ALWAYS_INLINE operator T() const { return this->Load(); }
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE T Load() const {
|
||||
return ConvertToType(impl::AtomicLoadImpl<Order>(this->GetStoragePointer()));
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE void Store(T arg) const {
|
||||
return impl::AtomicStoreImpl<Order>(this->GetStoragePointer(), ConvertToStorage(arg));
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE T Exchange(T arg) const {
|
||||
return ConvertToType(impl::AtomicExchangeImpl<Order>(this->GetStoragePointer(), ConvertToStorage(arg)));
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE bool CompareExchangeWeak(T &expected, T desired) const {
|
||||
return impl::AtomicCompareExchangeWeakImpl<Order, T>(this->GetStoragePointer(), expected, desired);
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE bool CompareExchangeStrong(T &expected, T desired) const {
|
||||
return impl::AtomicCompareExchangeStrongImpl<Order, T>(this->GetStoragePointer(), expected, desired);
|
||||
}
|
||||
|
||||
#define AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(_OPERATION_, _OPERATOR_, _POINTER_ALLOWED_) \
|
||||
template<bool Enable = (IsIntegral || (_POINTER_ALLOWED_ && IsPointer)), typename = typename std::enable_if<Enable, void>::type> \
|
||||
ALWAYS_INLINE T Fetch ## _OPERATION_(DifferenceType arg) const { \
|
||||
static_assert(Enable == (IsIntegral || (_POINTER_ALLOWED_ && IsPointer))); \
|
||||
volatile StorageType * const p = this->GetStoragePointer(); \
|
||||
\
|
||||
StorageType current; \
|
||||
do { \
|
||||
current = impl::LoadAcquireExclusiveForAtomic<StorageType>(p); \
|
||||
} while (AMS_UNLIKELY(!impl::StoreReleaseExclusiveForAtomic<StorageType>(p, ConvertToStorage(ConvertToType(current) _OPERATOR_ arg)))); \
|
||||
return ConvertToType(current); \
|
||||
} \
|
||||
\
|
||||
template<bool Enable = (IsIntegral || (_POINTER_ALLOWED_ && IsPointer)), typename = typename std::enable_if<Enable, void>::type> \
|
||||
ALWAYS_INLINE T operator _OPERATOR_##=(DifferenceType arg) const { \
|
||||
static_assert(Enable == (IsIntegral || (_POINTER_ALLOWED_ && IsPointer))); \
|
||||
return this->Fetch ## _OPERATION_(arg) _OPERATOR_ arg; \
|
||||
}
|
||||
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Add, +, true)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Sub, -, true)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(And, &, false)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Or, |, false)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Xor, ^, false)
|
||||
|
||||
#undef AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator++() const { static_assert(Enable == HasArithmeticFunctions); return this->FetchAdd(1) + 1; }
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator++(int) const { static_assert(Enable == HasArithmeticFunctions); return this->FetchAdd(1); }
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator--() const { static_assert(Enable == HasArithmeticFunctions); return this->FetchSub(1) - 1; }
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator--(int) const { static_assert(Enable == HasArithmeticFunctions); return this->FetchSub(1); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T>
|
||||
struct AtomicIntegerStorage;
|
||||
|
||||
template<typename T> requires (sizeof(T) == sizeof(u8))
|
||||
struct AtomicIntegerStorage<T> {
|
||||
using Type = u8;
|
||||
};
|
||||
|
||||
template<typename T> requires (sizeof(T) == sizeof(u16))
|
||||
struct AtomicIntegerStorage<T> {
|
||||
using Type = u16;
|
||||
};
|
||||
|
||||
template<typename T> requires (sizeof(T) == sizeof(u32))
|
||||
struct AtomicIntegerStorage<T> {
|
||||
using Type = u32;
|
||||
};
|
||||
|
||||
template<typename T> requires (sizeof(T) == sizeof(u64))
|
||||
struct AtomicIntegerStorage<T> {
|
||||
using Type = u64;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept UsableAtomicType = (sizeof(T) <= sizeof(u64)) && !std::is_const<T>::value && !std::is_volatile<T>::value && (std::is_pointer<T>::value || requires (const T &t) {
|
||||
std::bit_cast<typename AtomicIntegerStorage<T>::Type, T>(t);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
template<impl::UsableAtomicType T>
|
||||
class Atomic {
|
||||
NON_COPYABLE(Atomic);
|
||||
NON_MOVEABLE(Atomic);
|
||||
private:
|
||||
static constexpr bool IsIntegral = std::integral<T>;
|
||||
static constexpr bool IsPointer = std::is_pointer<T>::value;
|
||||
|
||||
static constexpr bool HasArithmeticFunctions = IsIntegral || IsPointer;
|
||||
|
||||
using DifferenceType = typename std::conditional<IsIntegral, T, typename std::conditional<IsPointer, std::ptrdiff_t, void>::type>::type;
|
||||
private:
|
||||
static_assert(std::atomic<T>::is_always_lock_free);
|
||||
private:
|
||||
std::atomic<T> m_v;
|
||||
public:
|
||||
ALWAYS_INLINE Atomic() { /* ... */ }
|
||||
constexpr ALWAYS_INLINE Atomic(T v) : m_v(v) { /* ... */ }
|
||||
|
||||
ALWAYS_INLINE T operator=(T desired) {
|
||||
return (m_v = desired);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE operator T() const { return this->Load(); }
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE T Load() const {
|
||||
return m_v.load(Order);
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE void Store(T arg) {
|
||||
return m_v.store(arg, Order);
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE T Exchange(T arg) {
|
||||
return m_v.exchange(arg, Order);
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE bool CompareExchangeWeak(T &expected, T desired) {
|
||||
return m_v.compare_exchange_weak(expected, desired, Order);
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE bool CompareExchangeStrong(T &expected, T desired) {
|
||||
return m_v.compare_exchange_strong(expected, desired, Order);
|
||||
}
|
||||
|
||||
#define AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(_OPERATION_, _OPERATION_LOWER_, _OPERATOR_, _POINTER_ALLOWED_) \
|
||||
template<bool Enable = (IsIntegral || (_POINTER_ALLOWED_ && IsPointer)), typename = typename std::enable_if<Enable, void>::type> \
|
||||
ALWAYS_INLINE T Fetch ## _OPERATION_(DifferenceType arg) { \
|
||||
static_assert(Enable == (IsIntegral || (_POINTER_ALLOWED_ && IsPointer))); \
|
||||
return m_v.fetch_##_OPERATION_LOWER_(arg); \
|
||||
} \
|
||||
\
|
||||
template<bool Enable = (IsIntegral || (_POINTER_ALLOWED_ && IsPointer)), typename = typename std::enable_if<Enable, void>::type> \
|
||||
ALWAYS_INLINE T operator _OPERATOR_##=(DifferenceType arg) { \
|
||||
static_assert(Enable == (IsIntegral || (_POINTER_ALLOWED_ && IsPointer))); \
|
||||
return this->Fetch##_OPERATION_(arg) _OPERATOR_ arg; \
|
||||
}
|
||||
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Add, add, +, true)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Sub, sub, -, true)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(And, and, &, false)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Or, or, |, false)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Xor, xor, ^, false)
|
||||
|
||||
#undef AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator++() { static_assert(Enable == HasArithmeticFunctions); return this->FetchAdd(1) + 1; }
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator++(int) { static_assert(Enable == HasArithmeticFunctions); return this->FetchAdd(1); }
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator--() { static_assert(Enable == HasArithmeticFunctions); return this->FetchSub(1) - 1; }
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator--(int) { static_assert(Enable == HasArithmeticFunctions); return this->FetchSub(1); }
|
||||
};
|
||||
|
||||
/* TODO: Clang does not yet define std::atomic_ref, so if we want this we will have to implement it ourselves. */
|
||||
#if !defined(ATMOSPHERE_COMPILER_CLANG)
|
||||
template<impl::UsableAtomicType T>
|
||||
class AtomicRef {
|
||||
NON_MOVEABLE(AtomicRef);
|
||||
public:
|
||||
static constexpr size_t RequiredAlignment = std::atomic_ref<T>::required_alignment;
|
||||
private:
|
||||
static constexpr bool IsIntegral = std::integral<T>;
|
||||
static constexpr bool IsPointer = std::is_pointer<T>::value;
|
||||
|
||||
static constexpr bool HasArithmeticFunctions = IsIntegral || IsPointer;
|
||||
|
||||
using DifferenceType = typename std::conditional<IsIntegral, T, typename std::conditional<IsPointer, std::ptrdiff_t, void>::type>::type;
|
||||
private:
|
||||
static_assert(std::atomic_ref<T>::is_always_lock_free);
|
||||
private:
|
||||
std::atomic_ref<T> m_ref;
|
||||
public:
|
||||
explicit ALWAYS_INLINE AtomicRef(T &t) : m_ref(t) { /* ... */ }
|
||||
ALWAYS_INLINE AtomicRef(const AtomicRef &) noexcept = default;
|
||||
|
||||
AtomicRef() = delete;
|
||||
AtomicRef &operator=(const AtomicRef &) = delete;
|
||||
|
||||
ALWAYS_INLINE T operator=(T desired) const { return (m_ref = desired); }
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE T Load() const {
|
||||
return m_ref.load(Order);
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE void Store(T arg) const {
|
||||
return m_ref.store(arg, Order);
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE T Exchange(T arg) const {
|
||||
return m_ref.exchange(arg, Order);
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE bool CompareExchangeWeak(T &expected, T desired) const {
|
||||
return m_ref.compare_exchange_weak(expected, desired, Order);
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE bool CompareExchangeStrong(T &expected, T desired) const {
|
||||
return m_ref.compare_exchange_strong(expected, desired, Order);
|
||||
}
|
||||
|
||||
#define AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(_OPERATION_, _OPERATION_LOWER_, _OPERATOR_, _POINTER_ALLOWED_) \
|
||||
template<bool Enable = (IsIntegral || (_POINTER_ALLOWED_ && IsPointer)), typename = typename std::enable_if<Enable, void>::type> \
|
||||
ALWAYS_INLINE T Fetch ## _OPERATION_(DifferenceType arg) const { \
|
||||
static_assert(Enable == (IsIntegral || (_POINTER_ALLOWED_ && IsPointer))); \
|
||||
return m_ref.fetch_##_OPERATION_LOWER_(arg); \
|
||||
} \
|
||||
\
|
||||
template<bool Enable = (IsIntegral || (_POINTER_ALLOWED_ && IsPointer)), typename = typename std::enable_if<Enable, void>::type> \
|
||||
ALWAYS_INLINE T operator _OPERATOR_##=(DifferenceType arg) const { \
|
||||
static_assert(Enable == (IsIntegral || (_POINTER_ALLOWED_ && IsPointer))); \
|
||||
return this->Fetch##_OPERATION_(arg) _OPERATOR_ arg; \
|
||||
}
|
||||
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Add, add, +, true)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Sub, sub, -, true)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(And, and, &, false)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Or, or, |, false)
|
||||
AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION(Xor, xor, ^, false)
|
||||
|
||||
#undef AMS_UTIL_IMPL_DEFINE_ATOMIC_FETCH_OPERATE_FUNCTION
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator++() const { static_assert(Enable == HasArithmeticFunctions); return this->FetchAdd(1) + 1; }
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator++(int) const { static_assert(Enable == HasArithmeticFunctions); return this->FetchAdd(1); }
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator--() const { static_assert(Enable == HasArithmeticFunctions); return this->FetchSub(1) - 1; }
|
||||
|
||||
template<bool Enable = HasArithmeticFunctions, typename = typename std::enable_if<Enable, void>::type>
|
||||
ALWAYS_INLINE T operator--(int) const { static_assert(Enable == HasArithmeticFunctions); return this->FetchSub(1); }
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_fourcc.hpp>
|
||||
|
||||
namespace ams::util::impl {
|
||||
|
||||
class AvailableIndexFinder {
|
||||
private:
|
||||
static constexpr int MaxDepthOfBox = 5;
|
||||
|
||||
struct BitFlags64 {
|
||||
private:
|
||||
u64 m_data;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE bool GetFlag(int index) const {
|
||||
AMS_ASSERT(index < 64);
|
||||
return ((m_data >> index) & UINT64_C(1)) != 0;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void SetFlag(int index) {
|
||||
AMS_ASSERT(index < 64);
|
||||
m_data |= (UINT64_C(1) << index);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void ClearFlag(int index) {
|
||||
AMS_ASSERT(index < 64);
|
||||
m_data &= ~(UINT64_C(1) << index);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsFull() const {
|
||||
return m_data == ~(UINT64_C(0));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE int FindIndexOfBitZero() const {
|
||||
AMS_ASSERT(!this->IsFull());
|
||||
return __builtin_ctzll(~m_data);
|
||||
}
|
||||
};
|
||||
private:
|
||||
int *m_p_current_index;
|
||||
int *m_p_map_index;
|
||||
void *m_buffer;
|
||||
int m_depth;
|
||||
BitFlags64 *m_flags;
|
||||
private:
|
||||
static constexpr int Pow64(int e) {
|
||||
switch (e) {
|
||||
case 0: return 0x1;
|
||||
case 1: return 0x40;
|
||||
case 2: return 0x1000;
|
||||
case 3: return 0x40000;
|
||||
case 4: return 0x1000000;
|
||||
case 5: return 0x40000000;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr u64 Roundup64(u64 value) {
|
||||
return (value + (64 - 1)) / 64;
|
||||
}
|
||||
|
||||
static constexpr size_t GetRequiredNumOfBox(int depth, size_t num_elements) {
|
||||
if (depth == 1) {
|
||||
return Roundup64(num_elements);
|
||||
} else if (depth == 2) {
|
||||
return Roundup64(num_elements) + Pow64(0);
|
||||
} else if (depth == 3) {
|
||||
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2);
|
||||
} else if (depth == 4) {
|
||||
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2) + Pow64(3);
|
||||
} else if (depth == 5) {
|
||||
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2) + Pow64(3) + Pow64(4);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr int CalcOffset(int *arr, int depth) {
|
||||
int offset = 0;
|
||||
for (auto i = 0; i < depth; ++i) {
|
||||
offset += Pow64(i);
|
||||
}
|
||||
for (auto i = 0; i < depth; ++i) {
|
||||
offset += Pow64(i) - arr[depth - 1 - i];
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
public:
|
||||
static consteval int GetSignature() {
|
||||
return static_cast<int>(util::ReverseFourCC<'B', 'I', 'T', 'S'>::Code);
|
||||
}
|
||||
|
||||
static constexpr int GetNeedDepth(size_t num_elements) {
|
||||
if (num_elements <= 0x40) {
|
||||
return 1;
|
||||
} else if (num_elements <= 0x1000) {
|
||||
return 2;
|
||||
} else if (num_elements <= 0x40000) {
|
||||
return 3;
|
||||
} else if (num_elements <= 0x1000000) {
|
||||
return 4;
|
||||
} else if (num_elements <= 0x40000000) {
|
||||
return 5;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
|
||||
return sizeof(BitFlags64) * GetRequiredNumOfBox(GetNeedDepth(num_elements), num_elements);
|
||||
}
|
||||
public:
|
||||
void Initialize(int *cur, int *map, u8 *buf) {
|
||||
const size_t num_elements = static_cast<size_t>(*map);
|
||||
AMS_ASSERT(num_elements <= static_cast<size_t>(Pow64(MaxDepthOfBox - 1)));
|
||||
|
||||
/* Set fields. */
|
||||
m_p_current_index = cur;
|
||||
m_p_map_index = map;
|
||||
m_buffer = buf;
|
||||
m_depth = GetNeedDepth(num_elements);
|
||||
|
||||
/* Validate fields. */
|
||||
AMS_ASSERT(m_depth > 0);
|
||||
|
||||
/* Setup memory. */
|
||||
std::memset(m_buffer, 0, GetRequiredMemorySize(num_elements));
|
||||
m_flags = reinterpret_cast<BitFlags64 *>(m_buffer);
|
||||
}
|
||||
|
||||
int AcquireIndex() {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(*m_p_current_index < *m_p_map_index);
|
||||
|
||||
/* Build up arrays. */
|
||||
int table[MaxDepthOfBox];
|
||||
BitFlags64 *pos[MaxDepthOfBox];
|
||||
for (auto i = 0; i < m_depth; ++i) {
|
||||
/* Determine the position. */
|
||||
pos[i] = std::addressof(m_flags[CalcOffset(table, i)]);
|
||||
|
||||
/* Set table entry. */
|
||||
table[i] = pos[i]->FindIndexOfBitZero();
|
||||
AMS_ASSERT(table[i] != BITSIZEOF(u64));
|
||||
}
|
||||
|
||||
/* Validate that the index is not acquired. */
|
||||
AMS_ASSERT(!pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
|
||||
|
||||
/* Acquire the index. */
|
||||
pos[m_depth - 1]->SetFlag(table[m_depth - 1]);
|
||||
|
||||
/* Validate that the index was acquired. */
|
||||
AMS_ASSERT(pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
|
||||
|
||||
/* Update tracking flags. */
|
||||
for (auto i = m_depth - 1; i > 0; --i) {
|
||||
if (pos[i]->IsFull()) {
|
||||
pos[i - 1]->SetFlag(table[i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the index we acquired. */
|
||||
int index = 0, pow = 0;
|
||||
for (auto i = m_depth; i > 0; --i, ++pow) {
|
||||
index += Pow64(pow) * table[i - 1];
|
||||
}
|
||||
|
||||
/* Increment current index. */
|
||||
++(*m_p_current_index);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void ReleaseIndex(int index) {
|
||||
/* Convert index to table. */
|
||||
int table[MaxDepthOfBox];
|
||||
for (auto i = 0; i < m_depth; ++i) {
|
||||
table[m_depth - 1 - i] = index % BITSIZEOF(u64);
|
||||
index /= BITSIZEOF(u64);
|
||||
}
|
||||
|
||||
/* Build up arrays. */
|
||||
BitFlags64 *pos[MaxDepthOfBox];
|
||||
for (auto i = 0; i < m_depth; ++i) {
|
||||
/* Determine the position. */
|
||||
pos[i] = std::addressof(m_flags[CalcOffset(table, i)]);
|
||||
}
|
||||
|
||||
/* Validate that the flag is set. */
|
||||
AMS_ASSERT(pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
|
||||
|
||||
/* Clear the flags. */
|
||||
for (auto i = m_depth - 1; i >= 0; --i) {
|
||||
pos[i]->ClearFlag(table[i]);
|
||||
}
|
||||
|
||||
/* Decrement current index. */
|
||||
--(*m_p_current_index);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util::impl {
|
||||
|
||||
template<bool Copy, bool CopyAssign, bool Move, bool MoveAssign, typename Tag = void>
|
||||
struct EnableCopyMove{};
|
||||
|
||||
template<typename Tag>
|
||||
struct EnableCopyMove<false, true, true, true, Tag> {
|
||||
constexpr EnableCopyMove() noexcept = default;
|
||||
|
||||
constexpr EnableCopyMove(const EnableCopyMove &) noexcept = delete;
|
||||
constexpr EnableCopyMove(EnableCopyMove &&) noexcept = default;
|
||||
constexpr EnableCopyMove &operator=(const EnableCopyMove &) noexcept = default;
|
||||
constexpr EnableCopyMove &operator=(EnableCopyMove &&) noexcept = default;
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
struct EnableCopyMove<true, false, true, true, Tag> {
|
||||
constexpr EnableCopyMove() noexcept = default;
|
||||
|
||||
constexpr EnableCopyMove(const EnableCopyMove &) noexcept = default;
|
||||
constexpr EnableCopyMove(EnableCopyMove &&) noexcept = default;
|
||||
constexpr EnableCopyMove &operator=(const EnableCopyMove &) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(EnableCopyMove &&) noexcept = default;
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
struct EnableCopyMove<false, false, true, true, Tag> {
|
||||
constexpr EnableCopyMove() noexcept = default;
|
||||
|
||||
constexpr EnableCopyMove(const EnableCopyMove &) noexcept = delete;
|
||||
constexpr EnableCopyMove(EnableCopyMove &&) noexcept = default;
|
||||
constexpr EnableCopyMove &operator=(const EnableCopyMove &) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(EnableCopyMove &&) noexcept = default;
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
struct EnableCopyMove<true, true, false, true, Tag> {
|
||||
constexpr EnableCopyMove() noexcept = default;
|
||||
|
||||
constexpr EnableCopyMove(const EnableCopyMove &) noexcept = default;
|
||||
constexpr EnableCopyMove(EnableCopyMove &&) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(const EnableCopyMove &) noexcept = default;
|
||||
constexpr EnableCopyMove &operator=(EnableCopyMove &&) noexcept = default;
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
struct EnableCopyMove<false, true, false, true, Tag> {
|
||||
constexpr EnableCopyMove() noexcept = default;
|
||||
|
||||
constexpr EnableCopyMove(const EnableCopyMove &) noexcept = delete;
|
||||
constexpr EnableCopyMove(EnableCopyMove &&) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(const EnableCopyMove &) noexcept = default;
|
||||
constexpr EnableCopyMove &operator=(EnableCopyMove &&) noexcept = default;
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
struct EnableCopyMove<true, false, false, true, Tag> {
|
||||
constexpr EnableCopyMove() noexcept = default;
|
||||
|
||||
constexpr EnableCopyMove(const EnableCopyMove &) noexcept = default;
|
||||
constexpr EnableCopyMove(EnableCopyMove &&) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(const EnableCopyMove &) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(EnableCopyMove &&) noexcept = default;
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
struct EnableCopyMove<false, false, false, true, Tag> {
|
||||
constexpr EnableCopyMove() noexcept = default;
|
||||
|
||||
constexpr EnableCopyMove(const EnableCopyMove &) noexcept = delete;
|
||||
constexpr EnableCopyMove(EnableCopyMove &&) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(const EnableCopyMove &) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(EnableCopyMove &&) noexcept = default;
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
struct EnableCopyMove<true, true, true, false, Tag> {
|
||||
constexpr EnableCopyMove() noexcept = default;
|
||||
|
||||
constexpr EnableCopyMove(const EnableCopyMove &) noexcept = default;
|
||||
constexpr EnableCopyMove(EnableCopyMove &&) noexcept = default;
|
||||
constexpr EnableCopyMove &operator=(const EnableCopyMove &) noexcept = default;
|
||||
constexpr EnableCopyMove &operator=(EnableCopyMove &&) noexcept = delete;
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
struct EnableCopyMove<true, true, false, false, Tag> {
|
||||
constexpr EnableCopyMove() noexcept = default;
|
||||
|
||||
constexpr EnableCopyMove(const EnableCopyMove &) noexcept = default;
|
||||
constexpr EnableCopyMove(EnableCopyMove &&) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(const EnableCopyMove &) noexcept = default;
|
||||
constexpr EnableCopyMove &operator=(EnableCopyMove &&) noexcept = delete;
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
struct EnableCopyMove<false, true, false, false, Tag> {
|
||||
constexpr EnableCopyMove() noexcept = default;
|
||||
|
||||
constexpr EnableCopyMove(const EnableCopyMove &) noexcept = delete;
|
||||
constexpr EnableCopyMove(EnableCopyMove &&) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(const EnableCopyMove &) noexcept = default;
|
||||
constexpr EnableCopyMove &operator=(EnableCopyMove &&) noexcept = delete;
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
struct EnableCopyMove<true, false, false, false, Tag> {
|
||||
constexpr EnableCopyMove() noexcept = default;
|
||||
|
||||
constexpr EnableCopyMove(const EnableCopyMove &) noexcept = default;
|
||||
constexpr EnableCopyMove(EnableCopyMove &&) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(const EnableCopyMove &) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(EnableCopyMove &&) noexcept = delete;
|
||||
};
|
||||
|
||||
template<typename Tag>
|
||||
struct EnableCopyMove<false, false, false, false, Tag> {
|
||||
constexpr EnableCopyMove() noexcept = default;
|
||||
|
||||
constexpr EnableCopyMove(const EnableCopyMove &) noexcept = delete;
|
||||
constexpr EnableCopyMove(EnableCopyMove &&) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(const EnableCopyMove &) noexcept = delete;
|
||||
constexpr EnableCopyMove &operator=(EnableCopyMove &&) noexcept = delete;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_alignment.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<size_t Alignment, size_t Size>
|
||||
class AlignedBuffer {
|
||||
private:
|
||||
static constexpr size_t AlignedSize = ((Size + Alignment - 1) / Alignment) * Alignment;
|
||||
static_assert(AlignedSize % Alignment == 0);
|
||||
private:
|
||||
u8 m_buffer[Alignment + AlignedSize];
|
||||
public:
|
||||
ALWAYS_INLINE operator u8 *() { return reinterpret_cast<u8 *>(util::AlignUp(reinterpret_cast<uintptr_t>(m_buffer), Alignment)); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_bitutil.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
/* Utilities for alignment to power of two. */
|
||||
template<typename T>
|
||||
constexpr ALWAYS_INLINE T AlignUp(T value, size_t alignment) {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
const U invmask = static_cast<U>(alignment - 1);
|
||||
return static_cast<T>((value + invmask) & ~invmask);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr ALWAYS_INLINE T AlignDown(T value, size_t alignment) {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
const U invmask = static_cast<U>(alignment - 1);
|
||||
return static_cast<T>(value & ~invmask);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr ALWAYS_INLINE bool IsAligned(T value, size_t alignment) {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
const U invmask = static_cast<U>(alignment - 1);
|
||||
return (value & invmask) == 0;
|
||||
}
|
||||
|
||||
template<typename T> requires std::unsigned_integral<T>
|
||||
constexpr ALWAYS_INLINE T GetAlignment(T value) {
|
||||
return value & -value;
|
||||
}
|
||||
|
||||
template<>
|
||||
ALWAYS_INLINE void *AlignUp<void *>(void *value, size_t alignment) {
|
||||
return reinterpret_cast<void *>(AlignUp(reinterpret_cast<uintptr_t>(value), alignment));
|
||||
}
|
||||
|
||||
template<>
|
||||
ALWAYS_INLINE const void *AlignUp<const void *>(const void *value, size_t alignment) {
|
||||
return reinterpret_cast<const void *>(AlignUp(reinterpret_cast<uintptr_t>(value), alignment));
|
||||
}
|
||||
|
||||
template<>
|
||||
ALWAYS_INLINE void *AlignDown<void *>(void *value, size_t alignment) {
|
||||
return reinterpret_cast<void *>(AlignDown(reinterpret_cast<uintptr_t>(value), alignment));
|
||||
}
|
||||
|
||||
template<>
|
||||
ALWAYS_INLINE const void *AlignDown<const void *>(const void *value, size_t alignment) {
|
||||
return reinterpret_cast<void *>(AlignDown(reinterpret_cast<uintptr_t>(value), alignment));
|
||||
}
|
||||
|
||||
template<>
|
||||
ALWAYS_INLINE bool IsAligned<void *>(void *value, size_t alignment) {
|
||||
return IsAligned(reinterpret_cast<uintptr_t>(value), alignment);
|
||||
}
|
||||
|
||||
template<>
|
||||
ALWAYS_INLINE bool IsAligned<const void *>(const void *value, size_t alignment) {
|
||||
return IsAligned(reinterpret_cast<uintptr_t>(value), alignment);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
|
||||
#include <vapours/util/arch/arm64/util_atomic.hpp>
|
||||
|
||||
#else
|
||||
|
||||
#include <vapours/util/arch/generic/util_atomic.hpp>
|
||||
|
||||
#endif
|
||||
@@ -1,197 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_alignment.hpp>
|
||||
#include <vapours/util/util_bitutil.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<size_t Count, typename Storage>
|
||||
constexpr ALWAYS_INLINE void NegateImpl(Storage arr[]) {
|
||||
for (size_t i = 0; i < Count; i++) {
|
||||
arr[i] = ~arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Count, typename Storage>
|
||||
constexpr ALWAYS_INLINE void AndImpl(Storage dst[], const Storage src[]) {
|
||||
for (size_t i = 0; i < Count; i++) {
|
||||
dst[i] &= src[i];
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Count, typename Storage>
|
||||
constexpr ALWAYS_INLINE void OrImpl(Storage dst[], const Storage src[]) {
|
||||
for (size_t i = 0; i < Count; i++) {
|
||||
dst[i] |= src[i];
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Count, typename Storage>
|
||||
constexpr ALWAYS_INLINE void XorImpl(Storage dst[], const Storage src[]) {
|
||||
for (size_t i = 0; i < Count; i++) {
|
||||
dst[i] ^= src[i];
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Count, typename Storage>
|
||||
constexpr ALWAYS_INLINE bool IsEqual(const Storage lhs[], const Storage rhs[]) {
|
||||
for (size_t i = 0; i < Count; i++) {
|
||||
if (lhs[i] != rhs[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<size_t Count, typename Storage>
|
||||
constexpr ALWAYS_INLINE bool IsAnySet(const Storage arr[]) {
|
||||
for (size_t i = 0; i < Count; i++) {
|
||||
if (arr[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<size_t Count, typename Storage>
|
||||
constexpr ALWAYS_INLINE int PopCount(const Storage arr[]) {
|
||||
int count = 0;
|
||||
for (size_t i = 0; i < Count; i++) {
|
||||
count += ::ams::util::PopCount(arr[i]);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<size_t N, typename T = void>
|
||||
struct BitFlagSet {
|
||||
static_assert(N > 0);
|
||||
using Storage = typename std::conditional<N <= BITSIZEOF(u32), u32, u64>::type;
|
||||
static constexpr size_t StorageBitCount = BITSIZEOF(Storage);
|
||||
static constexpr size_t StorageCount = util::AlignUp(N, StorageBitCount) / StorageBitCount;
|
||||
Storage _storage[StorageCount];
|
||||
private:
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &SetImpl(s32 idx, Storage mask, bool en) {
|
||||
if (en) {
|
||||
this->_storage[idx] |= mask;
|
||||
} else {
|
||||
this->_storage[idx] &= ~mask;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool TestImpl(s32 idx, Storage mask) const { return (this->_storage[idx] & mask) != 0; }
|
||||
constexpr ALWAYS_INLINE void Truncate() { TruncateIf(std::integral_constant<bool, N % StorageBitCount != 0>{}); }
|
||||
constexpr ALWAYS_INLINE void TruncateIf(std::true_type) { this->_storage[StorageCount - 1] &= MakeStorageMask(N) - 1; }
|
||||
constexpr ALWAYS_INLINE void TruncateIf(std::false_type) { /* ... */ }
|
||||
|
||||
static constexpr ALWAYS_INLINE s32 GetStorageIndex(s32 idx) { return idx / StorageBitCount; }
|
||||
static constexpr ALWAYS_INLINE Storage MakeStorageMask(s32 idx) { return static_cast<Storage>(1) << (idx % StorageBitCount); }
|
||||
public:
|
||||
class Reference {
|
||||
friend struct BitFlagSet<N, T>;
|
||||
private:
|
||||
BitFlagSet<N, T> *m_set;
|
||||
s32 m_idx;
|
||||
private:
|
||||
constexpr ALWAYS_INLINE Reference() : m_set(nullptr), m_idx(0) { /* ... */ }
|
||||
constexpr ALWAYS_INLINE Reference(BitFlagSet<N, T> &s, s32 i) : m_set(std::addressof(s)), m_idx(i) { /* ... */ }
|
||||
public:
|
||||
constexpr ALWAYS_INLINE Reference &operator=(bool en) { m_set->Set(m_idx, en); return *this; }
|
||||
constexpr ALWAYS_INLINE Reference &operator=(const Reference &r) { m_set->Set(m_idx, r); return *this; }
|
||||
constexpr ALWAYS_INLINE Reference &Negate() { m_set->Negate(m_idx); return *this; }
|
||||
constexpr ALWAYS_INLINE operator bool() const { return m_set->Test(m_idx); }
|
||||
constexpr ALWAYS_INLINE bool operator~() const { return !m_set->Test(m_idx); }
|
||||
};
|
||||
|
||||
template<s32 _Index>
|
||||
struct Flag {
|
||||
static_assert(_Index < static_cast<s32>(N));
|
||||
friend struct BitFlagSet<N, T>;
|
||||
static constexpr s32 Index = _Index;
|
||||
static const BitFlagSet<N, T> Mask;
|
||||
private:
|
||||
static constexpr s32 StorageIndex = Index / StorageBitCount;
|
||||
static constexpr Storage StorageMask = static_cast<Storage>(1) << (Index % StorageBitCount);
|
||||
|
||||
template<size_t StorageCount>
|
||||
struct SingleStorageTrait {
|
||||
static_assert(StorageCount == 1);
|
||||
using Type = Storage;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename FlagType>
|
||||
constexpr ALWAYS_INLINE bool Test() const { return this->TestImpl(FlagType::StorageIndex, FlagType::StorageMask); }
|
||||
constexpr ALWAYS_INLINE bool Test(s32 idx) const { return this->TestImpl(GetStorageIndex(idx), MakeStorageMask(idx)); }
|
||||
|
||||
template<typename FlagType>
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &Set(bool en = true) { return this->SetImpl(FlagType::StorageIndex, FlagType::StorageMask, en); }
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &Set(s32 idx, bool en = true) { return this->SetImpl(GetStorageIndex(idx), MakeStorageMask(idx), en); }
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &Set() { std::memset(this->_storage, ~0, sizeof(this->_storage)); this->Truncate(); return *this; }
|
||||
|
||||
template<typename FlagType>
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &Reset() { return this->Set<FlagType>(false); }
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &Reset(s32 idx) { return this->Set(idx, false); }
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &Reset() { std::memset(this->_storage, 0, sizeof(this->_storage)); this->Truncate(); return *this; }
|
||||
|
||||
template<typename FlagType>
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &Negate() { return this->Set<FlagType>(!this->Test<FlagType>()); }
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &Negate(s32 idx) { return this->Set(idx, !this->Test(idx)); }
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &Negate() { ams::util::impl::NegateImpl<StorageCount>(this->_storage); this->Truncate(); return *this; }
|
||||
|
||||
consteval static int GetCount() { return static_cast<int>(N); }
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsAnySet() const { return ams::util::impl::IsAnySet<StorageCount>(this->_storage); }
|
||||
constexpr ALWAYS_INLINE int PopCount() const { return ams::util::impl::PopCount<StorageCount>(this->_storage); }
|
||||
constexpr ALWAYS_INLINE bool IsAllSet() const { return this->PopCount() == this->GetCount(); }
|
||||
constexpr ALWAYS_INLINE bool IsAllOff() const { return !this->IsAnySet(); }
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator[](s32 idx) const { return this->Test(idx); }
|
||||
constexpr ALWAYS_INLINE Reference operator[](s32 idx) { return Reference(*this, idx); }
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator==(const BitFlagSet<N, T> &rhs) const { return ams::util::impl::IsEqual<StorageCount>(this->_storage, rhs._storage); }
|
||||
constexpr ALWAYS_INLINE bool operator!=(const BitFlagSet<N, T> &rhs) const { return !(*this == rhs); }
|
||||
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> operator~() const { BitFlagSet<N, T> tmp = *this; return tmp.Negate(); }
|
||||
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> operator&(const BitFlagSet<N, T> &rhs) const { BitFlagSet<N, T> v = *this; v &= rhs; return v; }
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> operator^(const BitFlagSet<N, T> &rhs) const { BitFlagSet<N, T> v = *this; v ^= rhs; return v; }
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> operator|(const BitFlagSet<N, T> &rhs) const { BitFlagSet<N, T> v = *this; v |= rhs; return v; }
|
||||
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &operator&=(const BitFlagSet<N, T> &rhs) { ams::util::impl::AndImpl<StorageCount>(this->_storage, rhs._storage); return *this; }
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &operator^=(const BitFlagSet<N, T> &rhs) { ams::util::impl::XorImpl<StorageCount>(this->_storage, rhs._storage); return *this; }
|
||||
constexpr ALWAYS_INLINE BitFlagSet<N, T> &operator|=(const BitFlagSet<N, T> &rhs) { ams::util::impl::OrImpl<StorageCount>(this->_storage, rhs._storage); return *this; }
|
||||
};
|
||||
|
||||
template<size_t N, typename T>
|
||||
template<s32 Index>
|
||||
constexpr inline const BitFlagSet<N, T> BitFlagSet<N, T>::Flag<Index>::Mask = { { static_cast<typename SingleStorageTrait<BitFlagSet<N, T>::StorageCount>::Type>(1) << Index } };
|
||||
|
||||
template<size_t N, typename T>
|
||||
constexpr BitFlagSet<N, T> MakeBitFlagSet() { return BitFlagSet<N, T>{}; }
|
||||
|
||||
template<size_t N>
|
||||
constexpr BitFlagSet<N, void> MakeBitFlagSet() { return MakeBitFlagSet<N, void>(); }
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename IntegralStorageType>
|
||||
struct BitPack {
|
||||
IntegralStorageType value;
|
||||
private:
|
||||
static_assert(std::is_integral<IntegralStorageType>::value);
|
||||
static_assert(std::is_unsigned<IntegralStorageType>::value);
|
||||
|
||||
template<size_t Index, size_t Count>
|
||||
static constexpr inline IntegralStorageType Mask = [] {
|
||||
static_assert(Index < BITSIZEOF(IntegralStorageType));
|
||||
static_assert(0 < Count && Count <= BITSIZEOF(IntegralStorageType));
|
||||
static_assert(Index + Count <= BITSIZEOF(IntegralStorageType));
|
||||
|
||||
if constexpr (Count == BITSIZEOF(IntegralStorageType)) {
|
||||
return ~IntegralStorageType(0);
|
||||
} else {
|
||||
return ((IntegralStorageType(1) << Count) - 1) << Index;
|
||||
}
|
||||
}();
|
||||
public:
|
||||
template<size_t _Index, size_t _Count, typename T = IntegralStorageType>
|
||||
struct Field {
|
||||
using Type = T;
|
||||
static constexpr size_t Index = _Index;
|
||||
static constexpr size_t Count = _Count;
|
||||
static constexpr size_t Next = Index + Count;
|
||||
|
||||
using BitPackType = BitPack<IntegralStorageType>;
|
||||
static_assert(util::is_pod<BitPackType>::value);
|
||||
|
||||
static_assert(Mask<Index, Count> != 0);
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value);
|
||||
static_assert(!std::is_same<T, bool>::value || Count == 1);
|
||||
};
|
||||
public:
|
||||
constexpr ALWAYS_INLINE void Clear() {
|
||||
constexpr IntegralStorageType Zero = IntegralStorageType(0);
|
||||
this->value = Zero;
|
||||
}
|
||||
|
||||
template<typename FieldType>
|
||||
constexpr ALWAYS_INLINE typename FieldType::Type Get() const {
|
||||
static_assert(std::is_same<FieldType, Field<FieldType::Index, FieldType::Count, typename FieldType::Type>>::value);
|
||||
return static_cast<typename FieldType::Type>((this->value & Mask<FieldType::Index, FieldType::Count>) >> FieldType::Index);
|
||||
}
|
||||
|
||||
template<typename FieldType>
|
||||
constexpr ALWAYS_INLINE void Set(typename FieldType::Type field_value) {
|
||||
static_assert(std::is_same<FieldType, Field<FieldType::Index, FieldType::Count, typename FieldType::Type>>::value);
|
||||
constexpr IntegralStorageType FieldMask = Mask<FieldType::Index, FieldType::Count>;
|
||||
this->value &= ~FieldMask;
|
||||
this->value |= (static_cast<IntegralStorageType>(field_value) << FieldType::Index) & FieldMask;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using BitPack8 = impl::BitPack<u8>;
|
||||
using BitPack16 = impl::BitPack<u16>;
|
||||
using BitPack32 = impl::BitPack<u32>;
|
||||
using BitPack64 = impl::BitPack<u64>;
|
||||
|
||||
static_assert(util::is_pod<BitPack8>::value);
|
||||
static_assert(util::is_pod<BitPack16>::value);
|
||||
static_assert(util::is_pod<BitPack32>::value);
|
||||
static_assert(util::is_pod<BitPack64>::value);
|
||||
static_assert(std::is_trivially_destructible<BitPack8 >::value);
|
||||
static_assert(std::is_trivially_destructible<BitPack16>::value);
|
||||
static_assert(std::is_trivially_destructible<BitPack32>::value);
|
||||
static_assert(std::is_trivially_destructible<BitPack64>::value);
|
||||
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_bitutil.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename Storage, size_t N>
|
||||
class BitSet {
|
||||
private:
|
||||
static_assert(std::is_integral<Storage>::value);
|
||||
static_assert(std::is_unsigned<Storage>::value);
|
||||
static_assert(sizeof(Storage) <= sizeof(u64));
|
||||
|
||||
static constexpr size_t FlagsPerWord = BITSIZEOF(Storage);
|
||||
static constexpr size_t NumWords = util::DivideUp(N, FlagsPerWord);
|
||||
|
||||
static constexpr ALWAYS_INLINE Storage GetBitMask(size_t bit) {
|
||||
return static_cast<Storage>(1) << (FlagsPerWord - 1 - bit);
|
||||
}
|
||||
private:
|
||||
Storage m_words[NumWords];
|
||||
public:
|
||||
constexpr ALWAYS_INLINE BitSet() : m_words() { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE void SetBit(size_t i) {
|
||||
m_words[i / FlagsPerWord] |= GetBitMask(i % FlagsPerWord);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void ClearBit(size_t i) {
|
||||
m_words[i / FlagsPerWord] &= ~GetBitMask(i % FlagsPerWord);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_t CountLeadingZero() const {
|
||||
for (size_t i = 0; i < NumWords; i++) {
|
||||
if (m_words[i]) {
|
||||
return FlagsPerWord * i + util::CountLeadingZeros<Storage>(m_words[i]);
|
||||
}
|
||||
}
|
||||
return FlagsPerWord * NumWords;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_t GetNextSet(size_t n) const {
|
||||
for (size_t i = (n + 1) / FlagsPerWord; i < NumWords; i++) {
|
||||
Storage word = m_words[i];
|
||||
if (!util::IsAligned(n + 1, FlagsPerWord)) {
|
||||
word &= GetBitMask(n % FlagsPerWord) - 1;
|
||||
}
|
||||
if (word) {
|
||||
return FlagsPerWord * i + util::CountLeadingZeros<Storage>(word);
|
||||
}
|
||||
}
|
||||
return FlagsPerWord * NumWords;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
using BitSet8 = impl::BitSet<u8, N>;
|
||||
|
||||
template<size_t N>
|
||||
using BitSet16 = impl::BitSet<u16, N>;
|
||||
|
||||
template<size_t N>
|
||||
using BitSet32 = impl::BitSet<u32, N>;
|
||||
|
||||
template<size_t N>
|
||||
using BitSet64 = impl::BitSet<u64, N>;
|
||||
|
||||
}
|
||||
@@ -1,287 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<size_t N>
|
||||
constexpr inline size_t Log2 = Log2<N / 2> + 1;
|
||||
|
||||
template<>
|
||||
constexpr inline size_t Log2<1> = 0;
|
||||
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr inline T ReverseBits(T x, int sw_bits = 1, int swar_words = 1) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(0 <= swar_words && swar_words < (BITSIZEOF(T) + 1));
|
||||
AMS_ASSERT(BITSIZEOF(T) % swar_words == 0);
|
||||
AMS_ASSERT(0 <= sw_bits && sw_bits < ((BITSIZEOF(T) / swar_words) + 1));
|
||||
AMS_ASSERT((BITSIZEOF(T) / swar_words) % sw_bits == 0);
|
||||
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
const int word_size = BITSIZEOF(T) / swar_words;
|
||||
const int k = word_size - sw_bits;
|
||||
|
||||
U u = std::bit_cast<U, T>(x);
|
||||
for (int i = 1; i < BITSIZEOF(T); i <<= 1) {
|
||||
const U mask = static_cast<U>(static_cast<U>(-1) / ((static_cast<U>(1) << i) + 1));
|
||||
|
||||
if (k & i) {
|
||||
u = static_cast<U>(((u & mask) << i) | ((u & static_cast<U>(~mask)) >> i));
|
||||
}
|
||||
}
|
||||
|
||||
return std::bit_cast<T, U>(u);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T ReverseBytes(T x, int sw_bytes = 1, int swar_words = 1) {
|
||||
return ReverseBits(x, sw_bytes * BITSIZEOF(u8), swar_words);
|
||||
}
|
||||
|
||||
template<typename T = u64, typename ...Args> requires std::integral<T>
|
||||
constexpr ALWAYS_INLINE T CombineBits(Args... args) {
|
||||
return (... | (T(1u) << args));
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T ResetLeastSignificantOneBit(T x) {
|
||||
return x & (x - 1);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T SetLeastSignificantZeroBit(T x) {
|
||||
return x | (x + 1);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T ResetTrailingOnes(T x) {
|
||||
return x & (x + 1);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T SetTrailingZeros(T x) {
|
||||
return x | (x - 1);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T LeastSignificantOneBit(T x) {
|
||||
return x & ~(x - 1);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T LeastSignificantZeroBit(T x) {
|
||||
return ~x & (x + 1);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T MaskTrailingZeros(T x) {
|
||||
return (~x) & (x - 1);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T MaskTrailingOnes(T x) {
|
||||
return x & ~(x + 1);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T MaskTrailingZerosAndLeastSignificantOneBit(T x) {
|
||||
return x ^ (x - 1);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T MaskTrailingOnesAndLeastSignificantZeroBit(T x) {
|
||||
return x ^ (x + 1);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE int PopCount(T x) {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
U u = static_cast<U>(x);
|
||||
|
||||
if (std::is_constant_evaluated()) {
|
||||
/* https://en.wikipedia.org/wiki/Hamming_weight */
|
||||
constexpr U m1 = U(-1) / 0x03;
|
||||
constexpr U m2 = U(-1) / 0x05;
|
||||
constexpr U m4 = U(-1) / 0x11;
|
||||
|
||||
u = static_cast<U>(u - ((u >> 1) & m1));
|
||||
u = static_cast<U>((u & m2) + ((u >> 2) & m2));
|
||||
u = static_cast<U>((u + (u >> 4)) & m4);
|
||||
|
||||
for (size_t i = 0; i < impl::Log2<sizeof(T)>; ++i) {
|
||||
const size_t shift = (0x1 << i) * BITSIZEOF(u8);
|
||||
u += u >> shift;
|
||||
}
|
||||
|
||||
return static_cast<int>(u & 0x7Fu);
|
||||
} else {
|
||||
if constexpr (std::is_same<U, unsigned long long>::value) {
|
||||
return __builtin_popcountll(u);
|
||||
} else if constexpr (std::is_same<U, unsigned long>::value) {
|
||||
return __builtin_popcountl(u);
|
||||
} else {
|
||||
static_assert(sizeof(U) <= sizeof(unsigned int));
|
||||
return __builtin_popcount(static_cast<unsigned int>(u));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE int CountLeadingZeros(T x) {
|
||||
if (std::is_constant_evaluated()) {
|
||||
for (size_t i = 0; i < impl::Log2<BITSIZEOF(T)>; ++i) {
|
||||
const size_t shift = (0x1 << i);
|
||||
x |= x >> shift;
|
||||
}
|
||||
return PopCount(static_cast<T>(~x));
|
||||
} else {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
|
||||
if (const U u = static_cast<U>(x); u != 0) {
|
||||
if constexpr (std::is_same<U, unsigned long long>::value) {
|
||||
return __builtin_clzll(u);
|
||||
} else if constexpr (std::is_same<U, unsigned long>::value) {
|
||||
return __builtin_clzl(u);
|
||||
} else if constexpr(std::is_same<U, unsigned int>::value) {
|
||||
return __builtin_clz(u);
|
||||
} else {
|
||||
static_assert(sizeof(U) < sizeof(unsigned int));
|
||||
constexpr size_t BitDiff = BITSIZEOF(unsigned int) - BITSIZEOF(U);
|
||||
return __builtin_clz(static_cast<unsigned int>(u)) - BitDiff;
|
||||
}
|
||||
} else {
|
||||
return BITSIZEOF(U);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(CountLeadingZeros(~static_cast<u64>(0)) == 0);
|
||||
static_assert(CountLeadingZeros(static_cast<u64>(1) << 5) == BITSIZEOF(u64) - 1 - 5);
|
||||
static_assert(CountLeadingZeros(static_cast<u64>(0)) == BITSIZEOF(u64));
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE int CountTrailingZeros(T x) {
|
||||
if (std::is_constant_evaluated()) {
|
||||
auto count = 0;
|
||||
for (size_t i = 0; i < BITSIZEOF(T) && (x & 1) == 0; ++i) {
|
||||
x >>= 1;
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
} else {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
if (const U u = static_cast<U>(x); u != 0) {
|
||||
if constexpr (std::is_same<U, unsigned long long>::value) {
|
||||
return __builtin_ctzll(u);
|
||||
} else if constexpr (std::is_same<U, unsigned long>::value) {
|
||||
return __builtin_ctzl(u);
|
||||
} else if constexpr(std::is_same<U, unsigned int>::value) {
|
||||
return __builtin_ctz(u);
|
||||
} else {
|
||||
static_assert(sizeof(U) < sizeof(unsigned int));
|
||||
return __builtin_ctz(static_cast<unsigned int>(u));
|
||||
}
|
||||
} else {
|
||||
return BITSIZEOF(U);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(CountTrailingZeros(~static_cast<u64>(0)) == 0);
|
||||
static_assert(CountTrailingZeros(static_cast<u64>(1) << 5) == 5);
|
||||
static_assert(CountTrailingZeros(static_cast<u64>(0)) == BITSIZEOF(u64));
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE bool IsPowerOfTwo(T x) {
|
||||
return x > 0 && ResetLeastSignificantOneBit(x) == 0;
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T CeilingPowerOfTwo(T x) {
|
||||
AMS_ASSERT(x > 0);
|
||||
return T(1) << (BITSIZEOF(T) - CountLeadingZeros(T(x - 1)));
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T FloorPowerOfTwo(T x) {
|
||||
AMS_ASSERT(x > 0);
|
||||
return T(1) << (BITSIZEOF(T) - CountLeadingZeros(x) - 1);
|
||||
}
|
||||
|
||||
template<std::integral T, std::integral U>
|
||||
constexpr ALWAYS_INLINE T DivideUp(T v, U d) {
|
||||
using Unsigned = typename std::make_unsigned<U>::type;
|
||||
using Sum = decltype(T{0} + U{0});
|
||||
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
AMS_ASSERT(v >= 0);
|
||||
AMS_ASSERT(d > 0);
|
||||
AMS_ASSERT(static_cast<Sum>(v) <= (std::numeric_limits<Sum>::max() - static_cast<Sum>(d) + static_cast<Sum>(1)));
|
||||
#endif
|
||||
|
||||
const Unsigned add = static_cast<Unsigned>(d) - 1;
|
||||
return static_cast<T>((static_cast<Sum>(v) + static_cast<Sum>(add)) / static_cast<Sum>(d));
|
||||
}
|
||||
|
||||
template<std::integral T, T N, T D>
|
||||
constexpr ALWAYS_INLINE T ScaleByConstantFactorUp(const T V) {
|
||||
/* Multiplying and dividing by large numerator/denominator can cause error to be introduced. */
|
||||
/* This algorithm multiples/divides in stages, so as to mitigate this (particularly with large denominator). */
|
||||
|
||||
/* Justification for the algorithm. */
|
||||
/* Calculate: (V * N) / D */
|
||||
/* = (Quot_V * D + Rem_V) * (Quot_N * D + Rem_N) / D */
|
||||
/* = (D^2 * (Quot_V * Quot_N) + D * (Quot_V * Rem_N + Rem_V * Quot_N) + Rem_V * Rem_N) / D */
|
||||
/* = (D * Quot_V * Quot_N) + (Quot_V * Rem_N) + (Rem_V * Quot_N) + ((Rem_V * Rem_N) / D) */
|
||||
|
||||
/* Calculate quotients/remainders. */
|
||||
const T Quot_V = V / D;
|
||||
const T Rem_V = V % D;
|
||||
constexpr T Quot_N = N / D;
|
||||
constexpr T Rem_N = N % D;
|
||||
|
||||
/* Calculate the remainder multiplication, rounding up. */
|
||||
const T rem_mult = ((Rem_V * Rem_N) + (D - 1)) / D;
|
||||
|
||||
/* Calculate results. */
|
||||
return (D * Quot_N * Quot_V) + (Quot_V * Rem_N) + (Rem_V * Quot_N) + rem_mult;
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T RotateLeft(T v, int n) {
|
||||
using Unsigned = typename std::make_unsigned<T>::type;
|
||||
static_assert(sizeof(Unsigned) == sizeof(T));
|
||||
|
||||
return static_cast<T>(std::rotl<Unsigned>(static_cast<Unsigned>(v), n));
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T RotateRight(T v, int n) {
|
||||
using Unsigned = typename std::make_unsigned<T>::type;
|
||||
static_assert(sizeof(Unsigned) == sizeof(T));
|
||||
|
||||
return static_cast<T>(std::rotr<Unsigned>(static_cast<Unsigned>(v), n));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_typed_storage.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<class Key, class Value, size_t N>
|
||||
class BoundedMap {
|
||||
private:
|
||||
std::array<util::optional<Key>, N> m_keys;
|
||||
std::array<TypedStorage<Value>, N> m_values;
|
||||
private:
|
||||
ALWAYS_INLINE void FreeEntry(size_t i) {
|
||||
m_keys[i].reset();
|
||||
DestroyAt(m_values[i]);
|
||||
}
|
||||
public:
|
||||
constexpr BoundedMap() : m_keys(), m_values() { /* ... */ }
|
||||
|
||||
Value *Find(const Key &key) {
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (m_keys[i] && m_keys[i].value() == key) {
|
||||
return GetPointer(m_values[i]);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Remove(const Key &key) {
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (m_keys[i] && m_keys[i].value() == key) {
|
||||
this->FreeEntry(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveAll() {
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
this->FreeEntry(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsFull() {
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (!m_keys[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Insert(const Key &key, Value &&value) {
|
||||
/* We can't insert if the key is used. */
|
||||
if (this->Find(key) != nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find a free value. */
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (!m_keys[i]) {
|
||||
m_keys[i] = key;
|
||||
ConstructAt(m_values[i], std::forward<Value>(value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InsertOrAssign(const Key &key, Value &&value) {
|
||||
/* Try to find and assign an existing value. */
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (m_keys[i] && m_keys[i].value() == key) {
|
||||
GetReference(m_values[i]) = std::forward<Value>(value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find a free value. */
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (!m_keys[i]) {
|
||||
m_keys[i] = key;
|
||||
ConstructAt(m_values[i], std::move(value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
bool Emplace(const Key &key, Args&&... args) {
|
||||
/* We can't emplace if the key is used. */
|
||||
if (this->Find(key) != nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find a free value. */
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (!m_keys[i]) {
|
||||
m_keys[i] = key;
|
||||
ConstructAt(m_values[i], std::forward<Args>(args)...);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void ForEach(F f) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (m_keys[i]) {
|
||||
f(m_keys[i].value(), GetReference(m_values[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void RemoveIf(F f) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (m_keys[i] && f(m_keys[i].value(), GetReference(m_values[i]))) {
|
||||
this->FreeEntry(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
enum CharacterEncodingResult {
|
||||
CharacterEncodingResult_Success = 0,
|
||||
CharacterEncodingResult_InsufficientLength = 1,
|
||||
CharacterEncodingResult_InvalidFormat = 2,
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
class CharacterEncodingHelper {
|
||||
public:
|
||||
static constexpr int8_t Utf8NBytesInnerTable[0x100 + 1] = {
|
||||
-1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8,
|
||||
};
|
||||
|
||||
static constexpr ALWAYS_INLINE char GetUtf8NBytes(size_t i) {
|
||||
return static_cast<char>(Utf8NBytesInnerTable[1 + i]);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
constexpr inline CharacterEncodingResult ConvertCharacterUtf8ToUtf32(u32 *dst, const char *src) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(dst != nullptr);
|
||||
AMS_ASSERT(src != nullptr);
|
||||
|
||||
/* Perform the conversion. */
|
||||
const auto *p = src;
|
||||
switch (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[0]))) {
|
||||
case 1:
|
||||
*dst = static_cast<u32>(p[0]);
|
||||
return CharacterEncodingResult_Success;
|
||||
case 2:
|
||||
if ((static_cast<u32>(p[0]) & 0x1E) != 0) {
|
||||
if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0) {
|
||||
*dst = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0);
|
||||
return CharacterEncodingResult_Success;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) {
|
||||
const u32 c = (static_cast<u32>(p[0] & 0xF) << 12) | (static_cast<u32>(p[1] & 0x3F) << 6) | (static_cast<u32>(p[2] & 0x3F) << 0);
|
||||
if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) {
|
||||
*dst = c;
|
||||
return CharacterEncodingResult_Success;
|
||||
}
|
||||
}
|
||||
return CharacterEncodingResult_InvalidFormat;
|
||||
case 4:
|
||||
if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 && impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) {
|
||||
const u32 c = (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) | (static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0);
|
||||
if (c >= 0x10000 && c < 0x110000) {
|
||||
*dst = c;
|
||||
return CharacterEncodingResult_Success;
|
||||
}
|
||||
}
|
||||
return CharacterEncodingResult_InvalidFormat;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* We failed to convert. */
|
||||
return CharacterEncodingResult_InvalidFormat;
|
||||
}
|
||||
|
||||
constexpr inline CharacterEncodingResult PickOutCharacterFromUtf8String(char *dst, const char **str) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(dst != nullptr);
|
||||
AMS_ASSERT(str != nullptr);
|
||||
AMS_ASSERT(*str != nullptr);
|
||||
|
||||
/* Clear the output. */
|
||||
dst[0] = 0;
|
||||
dst[1] = 0;
|
||||
dst[2] = 0;
|
||||
dst[3] = 0;
|
||||
|
||||
/* Perform the conversion. */
|
||||
const auto *p = *str;
|
||||
u32 c = static_cast<u32>(*p);
|
||||
switch (impl::CharacterEncodingHelper::GetUtf8NBytes(c)) {
|
||||
case 1:
|
||||
dst[0] = (*str)[0];
|
||||
++(*str);
|
||||
break;
|
||||
case 2:
|
||||
if ((p[0] & 0x1E) != 0) {
|
||||
if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0) {
|
||||
c = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0);
|
||||
dst[0] = (*str)[0];
|
||||
dst[1] = (*str)[1];
|
||||
(*str) += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CharacterEncodingResult_InvalidFormat;
|
||||
case 3:
|
||||
if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) {
|
||||
c = (static_cast<u32>(p[0] & 0xF) << 12) | (static_cast<u32>(p[1] & 0x3F) << 6) | (static_cast<u32>(p[2] & 0x3F) << 0);
|
||||
if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) {
|
||||
dst[0] = (*str)[0];
|
||||
dst[1] = (*str)[1];
|
||||
dst[2] = (*str)[2];
|
||||
(*str) += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CharacterEncodingResult_InvalidFormat;
|
||||
case 4:
|
||||
if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 && impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) {
|
||||
c = (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) | (static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0);
|
||||
if (c >= 0x10000 && c < 0x110000) {
|
||||
dst[0] = (*str)[0];
|
||||
dst[1] = (*str)[1];
|
||||
dst[2] = (*str)[2];
|
||||
dst[3] = (*str)[3];
|
||||
(*str) += 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CharacterEncodingResult_InvalidFormat;
|
||||
default:
|
||||
return CharacterEncodingResult_InvalidFormat;
|
||||
}
|
||||
|
||||
return CharacterEncodingResult_Success;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
consteval bool IsLittleEndian() {
|
||||
return std::endian::native == std::endian::little;
|
||||
}
|
||||
|
||||
consteval bool IsBigEndian() {
|
||||
return std::endian::native == std::endian::big;
|
||||
}
|
||||
|
||||
static_assert(IsLittleEndian() ^ IsBigEndian());
|
||||
|
||||
template<std::unsigned_integral U>
|
||||
constexpr ALWAYS_INLINE U SwapEndian(const U u) {
|
||||
static_assert(BITSIZEOF(u8) == 8);
|
||||
|
||||
if constexpr (sizeof(U) * BITSIZEOF(u8) == 64) {
|
||||
static_assert(__builtin_bswap64(UINT64_C(0x0123456789ABCDEF)) == UINT64_C(0xEFCDAB8967452301));
|
||||
return __builtin_bswap64(u);
|
||||
} else if constexpr (sizeof(U) * BITSIZEOF(u8) == 32) {
|
||||
static_assert(__builtin_bswap32(0x01234567u) == 0x67452301u);
|
||||
return __builtin_bswap32(u);
|
||||
} else if constexpr (sizeof(U) * BITSIZEOF(u8) == 16) {
|
||||
static_assert(__builtin_bswap16(0x0123u) == 0x2301u);
|
||||
return __builtin_bswap16(u);
|
||||
} else if constexpr (sizeof(U) * BITSIZEOF(u8) == 8) {
|
||||
return u;
|
||||
} else {
|
||||
static_assert(!std::is_same<U, U>::value);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u64 SwapEndian48(const u64 u) {
|
||||
using U = u64;
|
||||
static_assert(BITSIZEOF(u8) == 8);
|
||||
constexpr U ByteMask = 0xFFu;
|
||||
AMS_ASSERT((u & UINT64_C(0xFFFF000000000000)) == 0);
|
||||
return ((u & (ByteMask << 40)) >> 40) |
|
||||
((u & (ByteMask << 32)) >> 24) |
|
||||
((u & (ByteMask << 24)) >> 8) |
|
||||
((u & (ByteMask << 16)) << 8) |
|
||||
((u & (ByteMask << 8)) << 24) |
|
||||
((u & (ByteMask << 0)) << 40);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE void SwapEndian(T *ptr) {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
|
||||
*ptr = static_cast<T>(SwapEndian<U>(static_cast<U>(*ptr)));
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T ConvertToBigEndian(const T val) {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
|
||||
if constexpr (IsBigEndian()) {
|
||||
return static_cast<T>(static_cast<U>(val));
|
||||
} else {
|
||||
static_assert(IsLittleEndian());
|
||||
return static_cast<T>(SwapEndian<U>(static_cast<U>(val)));
|
||||
}
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T ConvertToLittleEndian(const T val) {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
|
||||
if constexpr (IsBigEndian()) {
|
||||
return static_cast<T>(SwapEndian<U>(static_cast<U>(val)));
|
||||
} else {
|
||||
static_assert(IsLittleEndian());
|
||||
return static_cast<T>(static_cast<U>(val));
|
||||
}
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T ConvertFromBigEndian(const T val) {
|
||||
return ConvertToBigEndian(val);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T ConvertFromLittleEndian(const T val) {
|
||||
return ConvertToLittleEndian(val);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T ConvertToBigEndian48(const T val) {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
static_assert(sizeof(T) == sizeof(u64));
|
||||
|
||||
if constexpr (IsBigEndian()) {
|
||||
AMS_ASSERT((static_cast<U>(val) & UINT64_C(0xFFFF000000000000)) == 0);
|
||||
return static_cast<T>(static_cast<U>(val));
|
||||
} else {
|
||||
static_assert(IsLittleEndian());
|
||||
return static_cast<T>(SwapEndian48(static_cast<U>(val)));
|
||||
}
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T ConvertToLittleEndian48(const T val) {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
static_assert(sizeof(T) == sizeof(u64));
|
||||
|
||||
if constexpr (IsBigEndian()) {
|
||||
return static_cast<T>(SwapEndian48(static_cast<U>(val)));
|
||||
} else {
|
||||
static_assert(IsLittleEndian());
|
||||
AMS_ASSERT((static_cast<U>(val) & UINT64_C(0xFFFF000000000000)) == 0);
|
||||
return static_cast<T>(static_cast<U>(val));
|
||||
}
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T LoadBigEndian(const T *ptr) {
|
||||
return ConvertToBigEndian<T>(*ptr);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T LoadLittleEndian(const T *ptr) {
|
||||
return ConvertToLittleEndian<T>(*ptr);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE void StoreBigEndian(T *ptr, T val) {
|
||||
*static_cast<volatile T *>(ptr) = ConvertToBigEndian<T>(val);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE void StoreLittleEndian(T *ptr, T val) {
|
||||
*static_cast<volatile T *>(ptr) = ConvertToLittleEndian<T>(val);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename Enum> requires std::is_enum<Enum>::value
|
||||
constexpr ALWAYS_INLINE typename std::underlying_type<Enum>::type ToUnderlying(Enum e) {
|
||||
return static_cast<typename std::underlying_type<Enum>::type>(e);
|
||||
}
|
||||
|
||||
template<typename Enum> requires std::is_enum<Enum>::value
|
||||
constexpr ALWAYS_INLINE Enum FromUnderlying(typename std::underlying_type<Enum>::type v) {
|
||||
return static_cast<Enum>(v);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename T, typename U> requires (std::is_assignable<T&, U&&>::value && std::is_copy_constructible<T>::value && std::is_move_constructible<T>::value)
|
||||
constexpr inline T Exchange(T *ptr, U value) {
|
||||
AMS_ASSERT(ptr != nullptr);
|
||||
auto ret = std::move(*ptr);
|
||||
*ptr = std::move(value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr inline T *Exchange(T **ptr, std::nullptr_t) {
|
||||
AMS_ASSERT(ptr != nullptr);
|
||||
auto ret(*ptr);
|
||||
*ptr = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_fixed_tree.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename Key, typename Value, typename Compare = std::less<Key>, size_t BufferAlignment = 8>
|
||||
class FixedMap {
|
||||
private:
|
||||
using KeyValuePair = std::pair<Key const, Value>;
|
||||
|
||||
struct LessTypeForMap {
|
||||
constexpr ALWAYS_INLINE bool operator()(const KeyValuePair &lhs, const KeyValuePair &rhs) const {
|
||||
return Compare{}(lhs.first, rhs.first);
|
||||
}
|
||||
};
|
||||
|
||||
using TreeType = ::ams::util::FixedTree<KeyValuePair, LessTypeForMap, KeyValuePair, BufferAlignment>;
|
||||
|
||||
using iterator = typename TreeType::iterator;
|
||||
using const_iterator = typename TreeType::const_iterator;
|
||||
public:
|
||||
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
|
||||
return TreeType::GetRequiredMemorySize(num_elements);
|
||||
}
|
||||
private:
|
||||
TreeType m_tree;
|
||||
public:
|
||||
FixedMap() : m_tree() { /* ... */ }
|
||||
|
||||
void Initialize(size_t num_elements, void *buffer, size_t buffer_size) {
|
||||
return m_tree.Initialize(num_elements, buffer, buffer_size);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE iterator begin() { return m_tree.begin(); }
|
||||
ALWAYS_INLINE const_iterator begin() const { return m_tree.begin(); }
|
||||
|
||||
ALWAYS_INLINE iterator end() { return m_tree.end(); }
|
||||
ALWAYS_INLINE const_iterator end() const { return m_tree.end(); }
|
||||
|
||||
ALWAYS_INLINE bool erase(const Key &key) { const KeyValuePair pair(key, Value{}); return m_tree.erase(pair); }
|
||||
|
||||
ALWAYS_INLINE iterator find(const Key &key) { const KeyValuePair pair(key, Value{}); return m_tree.find(pair); }
|
||||
ALWAYS_INLINE const_iterator find(const Key &key) const { const KeyValuePair pair(key, Value{}); return m_tree.find(pair); }
|
||||
|
||||
ALWAYS_INLINE std::pair<iterator, bool> insert(const KeyValuePair &pair) { return m_tree.insert(pair); }
|
||||
|
||||
ALWAYS_INLINE size_t size() const { return m_tree.size(); }
|
||||
|
||||
ALWAYS_INLINE void clear() { return m_tree.clear(); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_fixed_tree.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename Element, typename Compare = std::less<Element>, size_t BufferAlignment = 8>
|
||||
class FixedSet : public ::ams::util::FixedTree<Element, Compare, const Element, BufferAlignment> {
|
||||
/* ... */
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,998 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/impl/util_available_index_finder.hpp>
|
||||
#include <vapours/util/util_alignment.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename Member, typename Compare, typename IteratorMember, size_t BufferAlignment = 8> requires std::convertible_to<Member &, IteratorMember &>
|
||||
class FixedTree {
|
||||
private:
|
||||
class IteratorBase;
|
||||
friend class IteratorBase;
|
||||
private:
|
||||
enum class Color : u8 {
|
||||
Red = 0,
|
||||
Black = 1,
|
||||
};
|
||||
|
||||
static constexpr inline int Index_Nil = -1;
|
||||
static constexpr inline int Index_Leaf = -2;
|
||||
static constexpr inline int Index_BeforeBegin = -3;
|
||||
static constexpr inline int Index_AfterEnd = -4;
|
||||
|
||||
static constexpr inline size_t max_size = 0x40000000;
|
||||
|
||||
struct Header {
|
||||
/* "Nintendo Red-Black tree" */
|
||||
static constexpr u32 Signature = util::ReverseFourCC<'N','N','R','B'>::Code;
|
||||
|
||||
u32 header_size;
|
||||
u32 header_signature;
|
||||
u32 _08;
|
||||
s32 max_elements;
|
||||
s32 cur_elements;
|
||||
s32 root_index;
|
||||
s32 left_most_index;
|
||||
s32 right_most_index;
|
||||
s32 index_signature;
|
||||
u32 buffer_size;
|
||||
u32 node_size;
|
||||
u32 element_size;
|
||||
u32 _30;
|
||||
u32 _34;
|
||||
u32 _38;
|
||||
u32 _3C;
|
||||
u32 _40;
|
||||
u32 _44;
|
||||
u32 _48;
|
||||
u32 _4C;
|
||||
|
||||
void InitializeHeader(u32 _08, s32 max_e, s32 cur_e, u32 ind_sig, u32 buf_sz, u32 node_sz, u32 e_sz, u32 _30, u32 _34, u32 _38, u32 _3C, u32 _40, u32 _44) {
|
||||
this->header_size = sizeof(Header);
|
||||
this->header_signature = Signature;
|
||||
this->_08 = _08;
|
||||
this->max_elements = max_e;
|
||||
this->cur_elements = cur_e;
|
||||
this->root_index = Index_Nil;
|
||||
this->left_most_index = Index_Nil;
|
||||
this->right_most_index = Index_Nil;
|
||||
this->index_signature = ind_sig;
|
||||
this->buffer_size = buf_sz;
|
||||
this->node_size = node_sz;
|
||||
this->element_size = e_sz;
|
||||
this->_30 = _30;
|
||||
this->_34 = _34;
|
||||
this->_38 = _38;
|
||||
this->_3C = _3C;
|
||||
this->_40 = _40;
|
||||
this->_44 = _44;
|
||||
this->_48 = 0;
|
||||
this->_4C = 0;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Header) == 0x50);
|
||||
|
||||
struct IndexPair {
|
||||
int first;
|
||||
int last;
|
||||
};
|
||||
|
||||
struct Node {
|
||||
Member m_data;
|
||||
int m_parent;
|
||||
int m_right;
|
||||
int m_left;
|
||||
Color m_color;
|
||||
|
||||
void SetLeft(int l, Node *n, int p) {
|
||||
m_left = l;
|
||||
n->m_parent = p;
|
||||
}
|
||||
|
||||
void SetRight(int r, Node *n, int p) {
|
||||
m_right = r;
|
||||
n->m_parent = p;
|
||||
}
|
||||
};
|
||||
|
||||
class Iterator;
|
||||
class ConstIterator;
|
||||
|
||||
class IteratorBase {
|
||||
private:
|
||||
friend class ConstIterator;
|
||||
private:
|
||||
const FixedTree *m_tree;
|
||||
int m_index;
|
||||
protected:
|
||||
constexpr ALWAYS_INLINE IteratorBase(const FixedTree *tree, int index) : m_tree(tree), m_index(index) { /* ... */ }
|
||||
|
||||
constexpr bool IsEqualImpl(const IteratorBase &rhs) const {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_tree);
|
||||
|
||||
/* Check for tree equality. */
|
||||
if (m_tree != rhs.m_tree) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check for nil. */
|
||||
if (m_tree->IsNil(m_index) && m_tree->IsNil(rhs.m_index)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check for index equality. */
|
||||
return m_index == rhs.m_index;
|
||||
}
|
||||
|
||||
constexpr IteratorMember &DereferenceImpl() const {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_tree);
|
||||
|
||||
if (!m_tree->IsNil(m_index)) {
|
||||
return m_tree->m_nodes[m_index].m_data;
|
||||
} else {
|
||||
AMS_ASSERT(false);
|
||||
return m_tree->GetNode(std::numeric_limits<int>::max())->m_data;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IteratorBase &IncrementImpl() {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_tree);
|
||||
|
||||
this->OperateIndex(true);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IteratorBase &DecrementImpl() {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_tree);
|
||||
|
||||
this->OperateIndex(false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr void OperateIndex(bool increment) {
|
||||
if (increment) {
|
||||
/* We're incrementing. */
|
||||
if (m_index == Index_BeforeBegin) {
|
||||
m_index = 0;
|
||||
} else {
|
||||
m_index = m_tree->UncheckedPP(m_index);
|
||||
if (m_tree->IsNil(m_index)) {
|
||||
m_index = Index_AfterEnd;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* We're decrementing. */
|
||||
if (m_index == Index_AfterEnd) {
|
||||
m_index = static_cast<int>(m_tree->size()) - 1;
|
||||
} else {
|
||||
m_index = m_tree->UncheckedMM(m_index);
|
||||
if (m_tree->IsNil(m_index)) {
|
||||
m_index = Index_BeforeBegin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Iterator : public IteratorBase {
|
||||
public:
|
||||
constexpr ALWAYS_INLINE Iterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ }
|
||||
constexpr ALWAYS_INLINE Iterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator(const Iterator &rhs) = default;
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator==(const Iterator &rhs) const {
|
||||
return this->IsEqualImpl(rhs);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IteratorMember &operator*() const {
|
||||
return static_cast<IteratorMember &>(this->DereferenceImpl());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IteratorMember *operator->() const {
|
||||
return std::addressof(this->operator *());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator &operator++() {
|
||||
return static_cast<Iterator &>(this->IncrementImpl());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator &operator--() {
|
||||
return static_cast<Iterator &>(this->DecrementImpl());
|
||||
}
|
||||
};
|
||||
|
||||
class ConstIterator : public IteratorBase {
|
||||
public:
|
||||
constexpr ALWAYS_INLINE ConstIterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ }
|
||||
constexpr ALWAYS_INLINE ConstIterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE ConstIterator(const ConstIterator &rhs) = default;
|
||||
constexpr ALWAYS_INLINE ConstIterator(const Iterator &rhs) : IteratorBase(rhs.m_tree, rhs.m_index) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator==(const ConstIterator &rhs) const {
|
||||
return this->IsEqualImpl(rhs);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator!=(const ConstIterator &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const IteratorMember &operator*() const {
|
||||
return static_cast<const IteratorMember &>(this->DereferenceImpl());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const IteratorMember *operator->() const {
|
||||
return std::addressof(this->operator *());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE ConstIterator &operator++() {
|
||||
return static_cast<ConstIterator &>(this->IncrementImpl());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE ConstIterator &operator--() {
|
||||
return static_cast<ConstIterator &>(this->DecrementImpl());
|
||||
}
|
||||
};
|
||||
public:
|
||||
using iterator = Iterator;
|
||||
using const_iterator = ConstIterator;
|
||||
private:
|
||||
impl::AvailableIndexFinder m_index_finder;
|
||||
Node m_dummy_leaf;
|
||||
Node *m_p_dummy_leaf;
|
||||
u8 *m_buffer;
|
||||
Header *m_header;
|
||||
Node *m_nodes;
|
||||
iterator m_end_iterator;
|
||||
public:
|
||||
FixedTree() : m_end_iterator(*this, Index_Nil) {
|
||||
this->SetDummyMemory();
|
||||
}
|
||||
protected:
|
||||
void InitializeImpl(int num_elements, void *buffer, size_t buffer_size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(num_elements > 0);
|
||||
AMS_ASSERT(static_cast<size_t>(num_elements) <= max_size);
|
||||
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(buffer), BufferAlignment));
|
||||
AMS_ASSERT(buffer_size == GetRequiredMemorySize(num_elements));
|
||||
|
||||
/* Set buffer. */
|
||||
m_buffer = static_cast<u8 *>(buffer);
|
||||
m_header = reinterpret_cast<Header *>(m_buffer);
|
||||
|
||||
/* Setup memory. */
|
||||
this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature());
|
||||
|
||||
/* Check that buffer was set up correctly. */
|
||||
AMS_ASSERT(static_cast<u32>(buffer_size) == m_header->buffer_size);
|
||||
|
||||
/* Setup dummy leaf. */
|
||||
this->SetDummyMemory();
|
||||
}
|
||||
public:
|
||||
static constexpr size_t SizeOfNodes(size_t num_elements) {
|
||||
return util::AlignUp(sizeof(Node) * num_elements, BufferAlignment);
|
||||
}
|
||||
|
||||
static constexpr size_t SizeOfIndex(size_t num_elements) {
|
||||
return impl::AvailableIndexFinder::GetRequiredMemorySize(num_elements);
|
||||
}
|
||||
|
||||
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
|
||||
return sizeof(Header) + SizeOfNodes(num_elements) + SizeOfIndex(num_elements);
|
||||
}
|
||||
private:
|
||||
void SetDummyMemory() {
|
||||
m_dummy_leaf.m_color = Color::Black;
|
||||
m_dummy_leaf.m_parent = Index_Nil;
|
||||
m_dummy_leaf.m_left = Index_Leaf;
|
||||
m_dummy_leaf.m_right = Index_Leaf;
|
||||
m_p_dummy_leaf = std::addressof(m_dummy_leaf);
|
||||
}
|
||||
|
||||
void InitializeMemory(int num_elements, u32 buffer_size, u32 signature) {
|
||||
/* Initialize the header. */
|
||||
m_header->InitializeHeader(1, num_elements, 0, signature, buffer_size, sizeof(Node), sizeof(Member), 4, 4, 4, 4, 4, BufferAlignment);
|
||||
|
||||
/* Setup index finder. */
|
||||
m_index_finder.Initialize(std::addressof(m_header->cur_elements), std::addressof(m_header->max_elements), m_buffer + sizeof(*m_header) + SizeOfNodes(num_elements));
|
||||
|
||||
/* Set nodes array. */
|
||||
m_nodes = reinterpret_cast<Node *>(m_buffer + sizeof(*m_header));
|
||||
}
|
||||
|
||||
Node *GetNode(int index) const {
|
||||
if (index >= 0) {
|
||||
return m_nodes + index;
|
||||
} else {
|
||||
return m_p_dummy_leaf;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsNil(int index) const {
|
||||
return index < 0;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsLeaf(int index) const {
|
||||
return index == Index_Leaf;
|
||||
}
|
||||
|
||||
int GetRoot() const { return m_header->root_index; }
|
||||
void SetRoot(int index) {
|
||||
if (index == Index_Leaf) {
|
||||
index = Index_Nil;
|
||||
}
|
||||
|
||||
m_header->root_index = index;
|
||||
}
|
||||
|
||||
int GetLMost() const { return m_header->left_most_index; }
|
||||
void SetLMost(int index) { m_header->left_most_index = index; }
|
||||
|
||||
int GetRMost() const { return m_header->right_most_index; }
|
||||
void SetRMost(int index) { m_header->right_most_index = index; }
|
||||
|
||||
int GetParent(int index) const {
|
||||
return this->GetNode(index)->m_parent;
|
||||
}
|
||||
|
||||
int AcquireIndex() { return m_index_finder.AcquireIndex(); }
|
||||
void ReleaseIndex(int index) { return m_index_finder.ReleaseIndex(index); }
|
||||
|
||||
int EraseByIndex(int target_index) {
|
||||
/* Setup tracking variables. */
|
||||
const auto next_index = this->UncheckedPP(target_index);
|
||||
auto *target_node = this->GetNode(target_index);
|
||||
|
||||
auto a_index = Index_Leaf;
|
||||
auto *a_node = this->GetNode(a_index);
|
||||
auto b_index = Index_Leaf;
|
||||
auto *b_node = this->GetNode(b_index);
|
||||
auto cur_index = target_index;
|
||||
auto *cur_node = this->GetNode(cur_index);
|
||||
|
||||
if (cur_node->m_left == Index_Leaf) {
|
||||
a_index = cur_node->m_right;
|
||||
a_node = this->GetNode(a_index);
|
||||
|
||||
m_p_dummy_leaf->m_parent = cur_index;
|
||||
} else {
|
||||
if (cur_node->m_right == Index_Leaf) {
|
||||
a_index = cur_node->m_left;
|
||||
} else {
|
||||
cur_index = next_index;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
a_index = cur_node->m_right;
|
||||
}
|
||||
a_node = this->GetNode(a_index);
|
||||
|
||||
m_p_dummy_leaf->m_parent = cur_index;
|
||||
}
|
||||
|
||||
/* Ensure the a node is updated (redundant) */
|
||||
a_node = this->GetNode(a_index);
|
||||
|
||||
/* Update relevant metrics/links. */
|
||||
if (cur_index == target_index) {
|
||||
/* No left, but has right. */
|
||||
b_index = target_node->m_parent;
|
||||
b_node = this->GetNode(b_index);
|
||||
|
||||
if (a_index != Index_Leaf) {
|
||||
a_node->m_parent = b_index;
|
||||
}
|
||||
|
||||
if (this->GetRoot() == target_index) {
|
||||
this->SetRoot(a_index);
|
||||
} else if (b_node->m_left == target_index) {
|
||||
b_node->m_left = a_index;
|
||||
} else {
|
||||
b_node->m_right = a_index;
|
||||
}
|
||||
|
||||
if (this->GetLMost() == target_index) {
|
||||
this->SetLMost((a_index != Index_Leaf) ? this->FindMinInSubtree(a_index) : b_index);
|
||||
}
|
||||
|
||||
if (this->GetRMost() == target_index) {
|
||||
this->SetRMost((a_index != Index_Leaf) ? this->FindMaxInSubtree(a_index) : b_index);
|
||||
}
|
||||
} else {
|
||||
/* Has left or doesn't have right. */
|
||||
|
||||
/* Fix left links. */
|
||||
this->GetNode(target_node->m_left)->m_parent = cur_index;
|
||||
cur_node->m_left = target_node->m_left;
|
||||
|
||||
if (cur_index == target_node->m_right) {
|
||||
b_index = cur_index;
|
||||
b_node = this->GetNode(b_index);
|
||||
} else {
|
||||
b_index = cur_node->m_parent;
|
||||
b_node = this->GetNode(b_index);
|
||||
|
||||
if (!this->IsNil(a_index)) {
|
||||
a_node->m_parent = b_index;
|
||||
}
|
||||
|
||||
b_node->m_left = a_index;
|
||||
cur_node->m_right = target_node->m_right;
|
||||
|
||||
this->GetNode(target_node->m_right)->m_parent = cur_index;
|
||||
}
|
||||
|
||||
if (this->GetRoot() == target_index) {
|
||||
this->SetRoot(cur_index);
|
||||
} else {
|
||||
if (this->GetNode(target_node->m_parent)->m_left == target_index) {
|
||||
this->GetNode(target_node->m_parent)->m_left = cur_index;
|
||||
} else {
|
||||
this->GetNode(target_node->m_parent)->m_right = cur_index;
|
||||
}
|
||||
}
|
||||
|
||||
cur_node->m_parent = target_node->m_parent;
|
||||
std::swap(cur_node->m_color, target_node->m_color);
|
||||
}
|
||||
|
||||
/* Ensure the tree remains balanced. */
|
||||
if (target_node->m_color == Color::Black) {
|
||||
while (true) {
|
||||
if (a_index == this->GetRoot() || a_node->m_color != Color::Black) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (a_index == b_node->m_left) {
|
||||
cur_index = b_node->m_right;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
|
||||
if (cur_node->m_color == Color::Red) {
|
||||
cur_node->m_color = Color::Black;
|
||||
b_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
||||
|
||||
this->RotateLeft(b_index);
|
||||
|
||||
cur_index = b_node->m_right;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
}
|
||||
|
||||
if (this->IsNil(cur_index)) {
|
||||
a_index = b_index;
|
||||
a_node = b_node;
|
||||
} else {
|
||||
if (this->GetNode(cur_node->m_left)->m_color != Color::Black || this->GetNode(cur_node->m_right)->m_color != Color::Black) {
|
||||
if (this->GetNode(cur_node->m_right)->m_color == Color::Black) {
|
||||
this->GetNode(cur_node->m_left)->m_color = Color::Black;
|
||||
cur_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
||||
|
||||
this->RotateRight(cur_index);
|
||||
|
||||
cur_index = b_node->m_right;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
}
|
||||
|
||||
cur_node->m_color = b_node->m_color;
|
||||
b_node->m_color = Color::Black;
|
||||
|
||||
this->GetNode(cur_node->m_right)->m_color = Color::Black;
|
||||
|
||||
this->RotateLeft(b_index);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
cur_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
||||
|
||||
a_index = b_index;
|
||||
a_node = b_node;
|
||||
}
|
||||
} else {
|
||||
cur_index = b_node->m_left;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
|
||||
if (cur_node->m_color == Color::Red) {
|
||||
cur_node->m_color = Color::Black;
|
||||
b_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
||||
|
||||
this->RotateRight(b_index);
|
||||
|
||||
cur_index = b_node->m_left;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
}
|
||||
|
||||
if (this->IsNil(cur_index)) {
|
||||
a_index = b_index;
|
||||
a_node = b_node;
|
||||
} else {
|
||||
if (this->GetNode(cur_node->m_right)->m_color != Color::Black || this->GetNode(cur_node->m_left)->m_color != Color::Black) {
|
||||
if (this->GetNode(cur_node->m_left)->m_color == Color::Black) {
|
||||
this->GetNode(cur_node->m_right)->m_color = Color::Black;
|
||||
cur_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
||||
|
||||
this->RotateLeft(cur_index);
|
||||
|
||||
cur_index = b_node->m_left;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
}
|
||||
|
||||
cur_node->m_color = b_node->m_color;
|
||||
b_node->m_color = Color::Black;
|
||||
|
||||
this->GetNode(cur_node->m_left)->m_color = Color::Black;
|
||||
|
||||
this->RotateRight(b_index);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
cur_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
||||
|
||||
a_index = b_index;
|
||||
a_node = b_node;
|
||||
}
|
||||
}
|
||||
|
||||
b_index = a_node->m_parent;
|
||||
b_node = this->GetNode(b_index);
|
||||
}
|
||||
|
||||
a_node->m_color = Color::Black;
|
||||
}
|
||||
|
||||
/* Release the index. */
|
||||
this->ReleaseIndex(target_index);
|
||||
return target_index;
|
||||
}
|
||||
|
||||
int FindIndex(const Member &elem) const {
|
||||
return this->FindIndexSub(this->GetRoot(), elem);
|
||||
}
|
||||
|
||||
int FindIndexSub(int index, const Member &elem) const {
|
||||
if (index != Index_Nil) {
|
||||
auto *node = this->GetNode(index);
|
||||
if (Compare{}(elem, node->m_data)) {
|
||||
if (!this->IsLeaf(node->m_left)) {
|
||||
return this->FindIndexSub(node->m_left, elem);
|
||||
}
|
||||
} else {
|
||||
if (!Compare{}(node->m_data, elem)) {
|
||||
return index;
|
||||
}
|
||||
|
||||
if (!this->IsLeaf(node->m_right)) {
|
||||
return this->FindIndexSub(node->m_right, elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Index_Nil;
|
||||
}
|
||||
|
||||
int FindMaxInSubtree(int index) const {
|
||||
int max = index;
|
||||
for (auto *node = this->GetNode(index); !this->IsNil(node->m_right); node = this->GetNode(node->m_right)) {
|
||||
max = node->m_right;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
int FindMinInSubtree(int index) const {
|
||||
int min = index;
|
||||
for (auto *node = this->GetNode(index); !this->IsNil(node->m_left); node = this->GetNode(node->m_left)) {
|
||||
min = node->m_left;
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
int InsertAt(bool before, int parent, const Member &elem) {
|
||||
/* Get an index for the new element. */
|
||||
const auto index = this->AcquireIndex();
|
||||
|
||||
/* Create the node. */
|
||||
auto *node = this->GetNode(index);
|
||||
node->m_color = Color::Red;
|
||||
node->m_parent = parent;
|
||||
node->m_right = Index_Leaf;
|
||||
node->m_left = Index_Leaf;
|
||||
std::memcpy(reinterpret_cast<u8 *>(std::addressof(node->m_data)), reinterpret_cast<const u8 *>(std::addressof(elem)), sizeof(node->m_data));
|
||||
|
||||
/* Fix up the parent node. */
|
||||
auto *parent_node = this->GetNode(parent);
|
||||
if (before) {
|
||||
parent_node->m_left = index;
|
||||
if (parent == this->GetLMost()) {
|
||||
this->SetLMost(index);
|
||||
}
|
||||
} else {
|
||||
parent_node->m_right = index;
|
||||
if (parent == this->GetRMost()) {
|
||||
this->SetRMost(index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure the tree is balanced. */
|
||||
int cur_index = index;
|
||||
while (true) {
|
||||
auto *cur_node = this->GetNode(cur_index);
|
||||
if (this->GetNode(cur_node->m_parent)->m_color != Color::Red) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto *p_node = this->GetNode(cur_node->m_parent);
|
||||
auto *g_node = this->GetNode(p_node->m_parent);
|
||||
if (cur_node->m_parent == g_node->m_left) {
|
||||
if (auto *gr_node = this->GetNode(g_node->m_right); gr_node->m_color == Color::Red) {
|
||||
p_node->m_color = Color::Black;
|
||||
gr_node->m_color = Color::Black;
|
||||
g_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
|
||||
|
||||
cur_index = p_node->m_parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur_index == p_node->m_right) {
|
||||
cur_index = cur_node->m_parent;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
this->RotateLeft(cur_index);
|
||||
}
|
||||
|
||||
p_node = this->GetNode(cur_node->m_parent);
|
||||
p_node->m_color = Color::Black;
|
||||
|
||||
g_node = this->GetNode(p_node->m_parent);
|
||||
g_node->m_color = Color::Red;
|
||||
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
|
||||
|
||||
this->RotateRight(p_node->m_parent);
|
||||
} else {
|
||||
if (auto *gl_node = this->GetNode(g_node->m_left); gl_node->m_color == Color::Red) {
|
||||
p_node->m_color = Color::Black;
|
||||
gl_node->m_color = Color::Black;
|
||||
g_node->m_color = Color::Red;
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
|
||||
|
||||
cur_index = p_node->m_parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur_index == p_node->m_left) {
|
||||
cur_index = cur_node->m_parent;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
this->RotateRight(cur_index);
|
||||
}
|
||||
|
||||
p_node = this->GetNode(cur_node->m_parent);
|
||||
p_node->m_color = Color::Black;
|
||||
|
||||
g_node = this->GetNode(p_node->m_parent);
|
||||
g_node->m_color = Color::Red;
|
||||
|
||||
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
|
||||
|
||||
this->RotateLeft(p_node->m_parent);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set root color. */
|
||||
this->GetNode(this->GetRoot())->m_color = Color::Black;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int InsertNoHint(bool before, const Member &elem) {
|
||||
int cur_index = this->GetRoot();
|
||||
int prev_index = Index_Nil;
|
||||
bool less = true;
|
||||
while (cur_index != Index_Nil && cur_index != Index_Leaf) {
|
||||
auto *node = this->GetNode(cur_index);
|
||||
prev_index = cur_index;
|
||||
|
||||
if (before) {
|
||||
less = Compare{}(node->m_data, elem);
|
||||
} else {
|
||||
less = Compare{}(elem, node->m_data);
|
||||
}
|
||||
|
||||
if (less) {
|
||||
cur_index = node->m_left;
|
||||
} else {
|
||||
cur_index = node->m_right;
|
||||
}
|
||||
}
|
||||
|
||||
if (cur_index == Index_Nil) {
|
||||
/* Create a new node. */
|
||||
const auto index = this->AcquireIndex();
|
||||
auto *node = this->GetNode(index);
|
||||
node->m_color = Color::Black;
|
||||
node->m_parent = Index_Nil;
|
||||
node->m_right = Index_Leaf;
|
||||
node->m_left = Index_Leaf;
|
||||
std::memcpy(reinterpret_cast<u8 *>(std::addressof(node->m_data)), reinterpret_cast<const u8 *>(std::addressof(elem)), sizeof(node->m_data));
|
||||
|
||||
this->SetRoot(index);
|
||||
this->SetLMost(index);
|
||||
this->SetRMost(index);
|
||||
|
||||
return index;
|
||||
} else {
|
||||
auto *compare_node = this->GetNode(prev_index);
|
||||
if (less) {
|
||||
if (prev_index == this->GetLMost()) {
|
||||
return this->InsertAt(less, prev_index, elem);
|
||||
} else {
|
||||
compare_node = this->GetNode(this->UncheckedMM(prev_index));
|
||||
}
|
||||
}
|
||||
|
||||
if (Compare{}(compare_node->m_data, elem)) {
|
||||
return this->InsertAt(less, prev_index, elem);
|
||||
} else {
|
||||
return Index_Nil;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RotateLeft(int index) {
|
||||
/* Determine indices. */
|
||||
const auto p_index = this->GetParent(index);
|
||||
const auto r_index = this->GetNode(index)->m_right;
|
||||
const auto l_index = this->GetNode(index)->m_left;
|
||||
const auto rl_index = this->GetNode(r_index)->m_left;
|
||||
const auto rr_index = this->GetNode(r_index)->m_right;
|
||||
|
||||
/* Get nodes. */
|
||||
auto *node = this->GetNode(index);
|
||||
auto *p_node = this->GetNode(p_index);
|
||||
auto *r_node = this->GetNode(r_index);
|
||||
auto *l_node = this->GetNode(l_index);
|
||||
auto *rl_node = this->GetNode(rl_index);
|
||||
auto *rr_node = this->GetNode(rr_index);
|
||||
|
||||
/* Perform the rotation. */
|
||||
if (p_index == Index_Nil) {
|
||||
r_node->m_parent = Index_Nil;
|
||||
m_header->root_index = r_index;
|
||||
} else if (p_node->m_left == index) {
|
||||
p_node->SetLeft(r_index, r_node, p_index);
|
||||
} else {
|
||||
p_node->SetRight(r_index, r_node, p_index);
|
||||
}
|
||||
r_node->SetLeft(index, node, r_index);
|
||||
r_node->SetRight(rr_index, rr_node, r_index);
|
||||
node->SetLeft(l_index, l_node, index);
|
||||
node->SetRight(rl_index, rl_node, index);
|
||||
}
|
||||
|
||||
void RotateRight(int index) {
|
||||
/* Determine indices. */
|
||||
const auto p_index = this->GetParent(index);
|
||||
const auto l_index = this->GetNode(index)->m_left;
|
||||
const auto ll_index = this->GetNode(l_index)->m_left;
|
||||
const auto lr_index = this->GetNode(l_index)->m_right;
|
||||
const auto r_index = this->GetNode(index)->m_right;
|
||||
|
||||
/* Get nodes. */
|
||||
auto *node = this->GetNode(index);
|
||||
auto *p_node = this->GetNode(p_index);
|
||||
auto *l_node = this->GetNode(l_index);
|
||||
auto *ll_node = this->GetNode(ll_index);
|
||||
auto *lr_node = this->GetNode(lr_index);
|
||||
auto *r_node = this->GetNode(r_index);
|
||||
|
||||
/* Perform the rotation. */
|
||||
if (p_index == Index_Nil) {
|
||||
l_node->m_parent = Index_Nil;
|
||||
m_header->root_index = l_index;
|
||||
} else if (p_node->m_left == index) {
|
||||
p_node->SetLeft(l_index, l_node, p_index);
|
||||
} else {
|
||||
p_node->SetRight(l_index, l_node, p_index);
|
||||
}
|
||||
l_node->SetLeft(ll_index, ll_node, l_index);
|
||||
l_node->SetRight(index, node, l_index);
|
||||
node->SetLeft(lr_index, lr_node, index);
|
||||
node->SetRight(r_index, r_node, index);
|
||||
}
|
||||
|
||||
int UncheckedMM(int index) const {
|
||||
auto *node = this->GetNode(index);
|
||||
if (this->IsNil(index)) {
|
||||
index = this->GetRMost();
|
||||
node = this->GetNode(index);
|
||||
} else if (this->IsNil(node->m_left)) {
|
||||
int parent = node->m_parent;
|
||||
Node *p;
|
||||
|
||||
for (p = this->GetNode(parent); !this->IsNil(parent) && index == p->m_left; p = this->GetNode(parent)) {
|
||||
index = parent;
|
||||
node = p;
|
||||
parent = p->m_parent;
|
||||
}
|
||||
|
||||
if (!this->IsNil(index)) {
|
||||
index = parent;
|
||||
node = p;
|
||||
}
|
||||
} else {
|
||||
index = this->FindMaxInSubtree(node->m_left);
|
||||
node = this->GetNode(index);
|
||||
}
|
||||
|
||||
if (this->IsNil(index)) {
|
||||
return Index_Leaf;
|
||||
} else {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
int UncheckedPP(int index) const {
|
||||
auto *node = this->GetNode(index);
|
||||
|
||||
if (!this->IsNil(index)) {
|
||||
if (this->IsNil(node->m_right)) {
|
||||
int parent = node->m_parent;
|
||||
Node *p;
|
||||
|
||||
for (p = this->GetNode(parent); !this->IsNil(parent) && index == p->m_right; p = this->GetNode(parent)) {
|
||||
index = parent;
|
||||
node = p;
|
||||
parent = p->m_parent;
|
||||
}
|
||||
|
||||
index = parent;
|
||||
node = p;
|
||||
} else {
|
||||
index = this->FindMinInSubtree(node->m_right);
|
||||
node = this->GetNode(index);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->IsNil(index)) {
|
||||
return Index_Leaf;
|
||||
} else {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
public:
|
||||
void Initialize(size_t num_elements, void *buffer, size_t buffer_size) {
|
||||
AMS_ASSERT(num_elements <= max_size);
|
||||
|
||||
return this->InitializeImpl(static_cast<int>(num_elements), buffer, buffer_size);
|
||||
}
|
||||
|
||||
iterator begin() { return iterator(*this); }
|
||||
const_iterator begin() const { return const_iterator(*this); }
|
||||
|
||||
iterator end() { return m_end_iterator; }
|
||||
const_iterator end() const { return m_end_iterator; }
|
||||
|
||||
size_t size() const { return m_header->cur_elements; }
|
||||
|
||||
void clear() {
|
||||
const auto num_elements = m_header->max_elements;
|
||||
const auto buffer_size = m_header->buffer_size;
|
||||
AMS_ASSERT(buffer_size == static_cast<u32>(GetRequiredMemorySize(num_elements)));
|
||||
|
||||
return this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature());
|
||||
}
|
||||
|
||||
bool erase(const Member &elem) {
|
||||
const auto range = this->equal_range(elem);
|
||||
if (range.first != range.last) {
|
||||
this->EraseByIndex(range.first);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
iterator find(const Member &elem) {
|
||||
if (const auto index = this->FindIndex(elem); index >= 0) {
|
||||
return iterator(*this, index);
|
||||
} else {
|
||||
return this->end();
|
||||
}
|
||||
}
|
||||
|
||||
const_iterator find(const Member &elem) const {
|
||||
if (const auto index = this->FindIndex(elem); index >= 0) {
|
||||
return const_iterator(*this, index);
|
||||
} else {
|
||||
return this->end();
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const Member &elem) {
|
||||
const auto index = this->InsertNoHint(false, elem);
|
||||
const auto it = iterator(*this, index);
|
||||
return std::make_pair(it, !this->IsNil(index));
|
||||
}
|
||||
|
||||
IndexPair equal_range(const Member &elem) {
|
||||
/* Get node to start iteration. */
|
||||
auto cur_index = this->GetRoot();
|
||||
auto cur_node = this->GetNode(cur_index);
|
||||
|
||||
auto min_index = Index_Leaf;
|
||||
auto min_node = this->GetNode(min_index);
|
||||
|
||||
auto max_index = Index_Leaf;
|
||||
auto max_node = this->GetNode(max_index);
|
||||
|
||||
/* Iterate until current is leaf, to find min/max. */
|
||||
while (cur_index != Index_Leaf) {
|
||||
if (Compare{}(cur_node->m_data, elem)) {
|
||||
cur_index = cur_node->m_right;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
} else {
|
||||
if (max_index == Index_Leaf && Compare{}(elem, cur_node->m_data)) {
|
||||
max_index = cur_index;
|
||||
max_node = this->GetNode(max_index);
|
||||
}
|
||||
min_index = cur_index;
|
||||
min_node = this->GetNode(min_index);
|
||||
|
||||
cur_index = cur_node->m_left;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate again, to find correct range extent for max. */
|
||||
cur_index = (max_index == Index_Leaf) ? this->GetRoot() : max_node->m_left;
|
||||
cur_node = this->GetNode(cur_index);
|
||||
while (cur_index != Index_Leaf) {
|
||||
if (Compare{}(elem, cur_node->m_data)) {
|
||||
max_index = cur_index;
|
||||
max_node = cur_node;
|
||||
cur_index = cur_node->m_left;
|
||||
} else {
|
||||
cur_index = cur_node->m_right;
|
||||
}
|
||||
cur_node = this->GetNode(cur_index);
|
||||
}
|
||||
|
||||
AMS_UNUSED(min_node);
|
||||
return IndexPair{min_index, max_index};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
int SNPrintf(char *dst, size_t dst_size, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
|
||||
int VSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl);
|
||||
|
||||
int TSNPrintf(char *dst, size_t dst_size, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
|
||||
int TVSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl);
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_endian.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<char A, char B, char C, char D>
|
||||
struct FourCC {
|
||||
static constexpr u32 Code = IsLittleEndian() ? ((static_cast<u32>(A) << 0x00) | (static_cast<u32>(B) << 0x08) | (static_cast<u32>(C) << 0x10) | (static_cast<u32>(D) << 0x18))
|
||||
: ((static_cast<u32>(A) << 0x18) | (static_cast<u32>(B) << 0x10) | (static_cast<u32>(C) << 0x08) | (static_cast<u32>(D) << 0x00));
|
||||
|
||||
static constexpr const char String[] = {A, B, C, D};
|
||||
|
||||
static_assert(sizeof(Code) == 4);
|
||||
static_assert(sizeof(String) == 4);
|
||||
};
|
||||
|
||||
template<char A, char B, char C, char D>
|
||||
struct ReverseFourCC {
|
||||
static constexpr u32 Code = IsLittleEndian() ? ((static_cast<u32>(A) << 0x18) | (static_cast<u32>(B) << 0x10) | (static_cast<u32>(C) << 0x08) | (static_cast<u32>(D) << 0x00))
|
||||
: ((static_cast<u32>(A) << 0x00) | (static_cast<u32>(B) << 0x08) | (static_cast<u32>(C) << 0x10) | (static_cast<u32>(D) << 0x18));
|
||||
|
||||
static constexpr const char String[] = {D, C, B, A};
|
||||
|
||||
static_assert(sizeof(Code) == 4);
|
||||
static_assert(sizeof(String) == 4);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_typed_storage.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
#define AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(_TYPE_, _NAME_, ...) static constinit _TYPE_ _NAME_ { __VA_ARGS__ }
|
||||
|
||||
/* NOTE: This must use placement new, to support private constructors. */
|
||||
#define AMS_FUNCTION_LOCAL_STATIC_IMPL(_LOCKTYPE_, _SCOPELOCKTYPE_, _TYPE_, _NAME_, ...) \
|
||||
static constinit ::ams::util::TypedStorage<_TYPE_> s_fls_storage_for_##_NAME_ {}; \
|
||||
static constinit bool s_fls_initialized_##_NAME_ = false; \
|
||||
static constinit _LOCKTYPE_ s_fls_init_lock_##_NAME_ {}; \
|
||||
if (AMS_UNLIKELY(!(s_fls_initialized_##_NAME_))) { \
|
||||
_SCOPELOCKTYPE_ sl_fls_for_##_NAME_ { s_fls_init_lock_##_NAME_ }; \
|
||||
if (AMS_LIKELY(!(s_fls_initialized_##_NAME_))) { \
|
||||
new (::ams::util::impl::GetPointerForConstructAt(s_fls_storage_for_##_NAME_)) _TYPE_( __VA_ARGS__ ); \
|
||||
s_fls_initialized_##_NAME_ = true; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
_TYPE_ & _NAME_ = util::GetReference(s_fls_storage_for_##_NAME_)
|
||||
|
||||
#if defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
|
||||
#define AMS_FUNCTION_LOCAL_STATIC(_TYPE_, _NAME_, ...) AMS_FUNCTION_LOCAL_STATIC_IMPL(KSpinLock, KScopedSpinLock, _TYPE_, _NAME_, ##__VA_ARGS__)
|
||||
|
||||
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
#define AMS_FUNCTION_LOCAL_STATIC(_TYPE_, _NAME_, ...) AMS_FUNCTION_LOCAL_STATIC_IMPL(os::SdkMutex, std::scoped_lock, _TYPE_, _NAME_, ##__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename T>
|
||||
class IFunction;
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename>
|
||||
struct GetIFunctionTypeForObject;
|
||||
|
||||
template<typename F, typename R, typename... Args>
|
||||
struct GetIFunctionTypeForObject<R (F::*)(Args...)> { using Type = R(Args...); };
|
||||
|
||||
template<typename F, typename R, typename... Args>
|
||||
struct GetIFunctionTypeForObject<R (F::*)(Args...) const> { using Type = R(Args...); };
|
||||
|
||||
template<typename>
|
||||
struct GetIFunctionType;
|
||||
|
||||
template<typename R, typename... Args>
|
||||
struct GetIFunctionType<R(Args...)> { using Type = R(Args...); };
|
||||
|
||||
template<typename R, typename... Args>
|
||||
struct GetIFunctionType<R(*)(Args...)> : public GetIFunctionType<R(Args...)>{};
|
||||
|
||||
template<typename F>
|
||||
struct GetIFunctionType<std::reference_wrapper<F>> : public GetIFunctionType<F>{};
|
||||
|
||||
template<typename F>
|
||||
struct GetIFunctionType : public GetIFunctionTypeForObject<decltype(&F::operator())>{};
|
||||
|
||||
template<typename T, typename F, typename Enabled = void>
|
||||
class Function;
|
||||
|
||||
template<typename R, typename... Args, typename F>
|
||||
class Function<R(Args...), F, typename std::enable_if<!(std::is_class<F>::value && !std::is_final<F>::value)>::type> final : public IFunction<R(Args...)> {
|
||||
private:
|
||||
F m_f;
|
||||
public:
|
||||
constexpr explicit Function(F f) : m_f(std::move(f)) { /* ... */}
|
||||
constexpr virtual ~Function() override { /* ... */ }
|
||||
|
||||
constexpr virtual R operator()(Args... args) const override final {
|
||||
return m_f(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename R, typename... Args, typename F>
|
||||
class Function<R(Args...), F, typename std::enable_if<std::is_class<F>::value && !std::is_final<F>::value>::type> final : public IFunction<R(Args...)>, private F {
|
||||
public:
|
||||
constexpr explicit Function(F f) : F(std::move(f)) { /* ... */}
|
||||
constexpr virtual ~Function() override { /* ... */ }
|
||||
|
||||
constexpr virtual R operator()(Args... args) const override final {
|
||||
return static_cast<const F &>(*this).operator()(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename I, typename F>
|
||||
constexpr ALWAYS_INLINE auto MakeIFunctionExplicitly(F f) {
|
||||
using FunctionType = ::ams::util::impl::Function<I, typename std::decay<F>::type>;
|
||||
return FunctionType{ std::move(f) };
|
||||
}
|
||||
|
||||
template<typename I, typename T, typename R>
|
||||
constexpr ALWAYS_INLINE auto MakeIFunctionExplicitly(R T::*f) {
|
||||
return MakeIFunctionExplicitly<I>(std::mem_fn(f));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
class IFunction<R(Args...)> {
|
||||
protected:
|
||||
constexpr virtual ~IFunction() { /* ... */ };
|
||||
public:
|
||||
constexpr virtual R operator()(Args... args) const = 0;
|
||||
|
||||
template<typename F>
|
||||
static constexpr ALWAYS_INLINE auto Make(F f) {
|
||||
return ::ams::util::impl::MakeIFunctionExplicitly<R(Args...)>(std::move(f));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F, typename = typename std::enable_if<!std::is_member_pointer<F>::value>::type>
|
||||
constexpr ALWAYS_INLINE auto MakeIFunction(F f) {
|
||||
static_assert(!std::is_member_pointer<F>::value);
|
||||
|
||||
return IFunction<typename ::ams::util::impl::GetIFunctionType<typename std::decay<F>::type>::Type>::Make(std::move(f));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
struct in_place_t{};
|
||||
|
||||
constexpr inline in_place_t in_place = {};
|
||||
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<std::signed_integral To, std::signed_integral From>
|
||||
constexpr ALWAYS_INLINE bool IsIntValueRepresentableImpl(From v) {
|
||||
using ToLimit = std::numeric_limits<To>;
|
||||
using FromLimit = std::numeric_limits<From>;
|
||||
if constexpr (ToLimit::min() <= FromLimit::min() && FromLimit::max() <= ToLimit::max()) {
|
||||
return true;
|
||||
} else {
|
||||
return ToLimit::min() <= v && v <= ToLimit::max();
|
||||
}
|
||||
}
|
||||
|
||||
template<std::unsigned_integral To, std::unsigned_integral From>
|
||||
constexpr ALWAYS_INLINE bool IsIntValueRepresentableImpl(From v) {
|
||||
using ToLimit = std::numeric_limits<To>;
|
||||
using FromLimit = std::numeric_limits<From>;
|
||||
if constexpr (ToLimit::min() <= FromLimit::min() && FromLimit::max() <= ToLimit::max()) {
|
||||
return true;
|
||||
} else {
|
||||
return ToLimit::min() <= v && v <= ToLimit::max();
|
||||
}
|
||||
}
|
||||
|
||||
template<std::unsigned_integral To, std::signed_integral From>
|
||||
constexpr ALWAYS_INLINE bool IsIntValueRepresentableImpl(From v) {
|
||||
using UnsignedFrom = typename std::make_unsigned<From>::type;
|
||||
|
||||
if (v < 0) {
|
||||
return false;
|
||||
} else {
|
||||
return IsIntValueRepresentableImpl<To, UnsignedFrom>(static_cast<UnsignedFrom>(v));
|
||||
}
|
||||
}
|
||||
|
||||
template<std::signed_integral To, std::unsigned_integral From>
|
||||
constexpr ALWAYS_INLINE bool IsIntValueRepresentableImpl(From v) {
|
||||
using UnsignedTo = typename std::make_unsigned<To>::type;
|
||||
|
||||
return v <= static_cast<UnsignedTo>(std::numeric_limits<To>::max());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<std::integral To, std::integral From>
|
||||
constexpr ALWAYS_INLINE bool IsIntValueRepresentable(From v) {
|
||||
return ::ams::util::impl::IsIntValueRepresentableImpl<To, From>(v);
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE bool CanAddWithoutOverflow(T x, T y) {
|
||||
if constexpr (std::unsigned_integral<T>) {
|
||||
return x <= std::numeric_limits<T>::max() - y;
|
||||
} else {
|
||||
if (y >= 0) {
|
||||
return x <= std::numeric_limits<T>::max() - y;
|
||||
} else {
|
||||
return x >= std::numeric_limits<T>::min() - y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE bool CanSubtractWithoutOverflow(T x, T y) {
|
||||
if constexpr (std::unsigned_integral<T>) {
|
||||
return x >= std::numeric_limits<T>::min() + y;
|
||||
} else {
|
||||
if (y >= 0) {
|
||||
return x >= std::numeric_limits<T>::min() + y;
|
||||
} else {
|
||||
return x <= std::numeric_limits<T>::max() + y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE bool CanMultiplyWithoutOverflow(T x, T y) {
|
||||
if (x == 0 || y == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if constexpr (std::unsigned_integral<T>) {
|
||||
return y <= std::numeric_limits<T>::max() / x;
|
||||
} else {
|
||||
if (x > 0) {
|
||||
if (y > 0) {
|
||||
return y <= std::numeric_limits<T>::max() / x;
|
||||
} else /*if (y < 0) */ {
|
||||
return y >= std::numeric_limits<T>::min() / x;
|
||||
}
|
||||
} else /* if (x < 0) */ {
|
||||
if (y > 0) {
|
||||
return x >= std::numeric_limits<T>::min() / y;
|
||||
} else /*if (y < 0) */ {
|
||||
return y >= std::numeric_limits<T>::max() / x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr inline bool TryAddWithoutOverflow(T *out, T x, T y) {
|
||||
AMS_ASSERT(out != nullptr);
|
||||
|
||||
if (CanAddWithoutOverflow(x, y)) {
|
||||
*out = x + y;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr inline bool TrySubtractWithoutOverflow(T *out, T x, T y) {
|
||||
AMS_ASSERT(out != nullptr);
|
||||
|
||||
if (CanSubtractWithoutOverflow(x, y)) {
|
||||
*out = x - y;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr inline bool TryMultiplyWithoutOverflow(T *out, T x, T y) {
|
||||
AMS_ASSERT(out != nullptr);
|
||||
|
||||
if (CanMultiplyWithoutOverflow(x, y)) {
|
||||
*out = x * y;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,623 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_parent_of_member.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
AMS_PRAGMA_BEGIN_OPTIMIZE("-O3")
|
||||
|
||||
/* Forward declare implementation class for Node. */
|
||||
namespace impl {
|
||||
|
||||
class IntrusiveListImpl;
|
||||
|
||||
}
|
||||
|
||||
class IntrusiveListNode {
|
||||
NON_COPYABLE(IntrusiveListNode);
|
||||
private:
|
||||
friend class impl::IntrusiveListImpl;
|
||||
|
||||
IntrusiveListNode *m_prev;
|
||||
IntrusiveListNode *m_next;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE IntrusiveListNode() : m_prev(this), m_next(this) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsLinked() const {
|
||||
return m_next != this;
|
||||
}
|
||||
private:
|
||||
constexpr ALWAYS_INLINE void LinkPrev(IntrusiveListNode *node) {
|
||||
/* We can't link an already linked node. */
|
||||
AMS_ASSERT(!node->IsLinked());
|
||||
this->SplicePrev(node, node);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void SplicePrev(IntrusiveListNode *first, IntrusiveListNode *last) {
|
||||
/* Splice a range into the list. */
|
||||
auto last_prev = last->m_prev;
|
||||
first->m_prev = m_prev;
|
||||
last_prev->m_next = this;
|
||||
m_prev->m_next = first;
|
||||
m_prev = last_prev;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void LinkNext(IntrusiveListNode *node) {
|
||||
/* We can't link an already linked node. */
|
||||
AMS_ASSERT(!node->IsLinked());
|
||||
return this->SpliceNext(node, node);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void SpliceNext(IntrusiveListNode *first, IntrusiveListNode *last) {
|
||||
/* Splice a range into the list. */
|
||||
auto last_prev = last->m_prev;
|
||||
first->m_prev = this;
|
||||
last_prev->m_next = m_next;
|
||||
m_next->m_prev = last_prev;
|
||||
m_next = first;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void Unlink() {
|
||||
this->Unlink(m_next);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void Unlink(IntrusiveListNode *last) {
|
||||
/* Unlink a node from a next node. */
|
||||
auto last_prev = last->m_prev;
|
||||
m_prev->m_next = last;
|
||||
last->m_prev = m_prev;
|
||||
last_prev->m_next = this;
|
||||
m_prev = last_prev;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IntrusiveListNode *GetPrev() {
|
||||
return m_prev;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const IntrusiveListNode *GetPrev() const {
|
||||
return m_prev;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IntrusiveListNode *GetNext() {
|
||||
return m_next;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const IntrusiveListNode *GetNext() const {
|
||||
return m_next;
|
||||
}
|
||||
};
|
||||
/* DEPRECATED: static_assert(std::is_literal_type<IntrusiveListNode>::value); */
|
||||
|
||||
namespace impl {
|
||||
|
||||
class IntrusiveListImpl {
|
||||
NON_COPYABLE(IntrusiveListImpl);
|
||||
private:
|
||||
IntrusiveListNode m_root_node;
|
||||
public:
|
||||
template<bool Const>
|
||||
class Iterator;
|
||||
|
||||
using value_type = IntrusiveListNode;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using const_pointer = const value_type *;
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using iterator = Iterator<false>;
|
||||
using const_iterator = Iterator<true>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
template<bool Const>
|
||||
class Iterator {
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = typename IntrusiveListImpl::value_type;
|
||||
using difference_type = typename IntrusiveListImpl::difference_type;
|
||||
using pointer = typename std::conditional<Const, IntrusiveListImpl::const_pointer, IntrusiveListImpl::pointer>::type;
|
||||
using reference = typename std::conditional<Const, IntrusiveListImpl::const_reference, IntrusiveListImpl::reference>::type;
|
||||
private:
|
||||
pointer m_node;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE explicit Iterator(pointer n) : m_node(n) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator==(const Iterator &rhs) const {
|
||||
return m_node == rhs.m_node;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE pointer operator->() const {
|
||||
return m_node;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reference operator*() const {
|
||||
return *m_node;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator &operator++() {
|
||||
m_node = m_node->m_next;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator &operator--() {
|
||||
m_node = m_node->m_prev;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator operator++(int) {
|
||||
const Iterator it{*this};
|
||||
++(*this);
|
||||
return it;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator operator--(int) {
|
||||
const Iterator it{*this};
|
||||
--(*this);
|
||||
return it;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE operator Iterator<true>() const {
|
||||
return Iterator<true>(m_node);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator<false> GetNonConstIterator() const {
|
||||
return Iterator<false>(const_cast<IntrusiveListImpl::pointer>(m_node));
|
||||
}
|
||||
};
|
||||
public:
|
||||
constexpr ALWAYS_INLINE IntrusiveListImpl() : m_root_node() { /* ... */ }
|
||||
|
||||
/* Iterator accessors. */
|
||||
constexpr ALWAYS_INLINE iterator begin() {
|
||||
return iterator(m_root_node.GetNext());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator begin() const {
|
||||
return const_iterator(m_root_node.GetNext());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator end() {
|
||||
return iterator(std::addressof(m_root_node));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator end() const {
|
||||
return const_iterator(std::addressof(m_root_node));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator iterator_to(reference v) {
|
||||
/* Only allow iterator_to for values in lists. */
|
||||
AMS_ASSERT(v.IsLinked());
|
||||
return iterator(std::addressof(v));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator iterator_to(const_reference v) const {
|
||||
/* Only allow iterator_to for values in lists. */
|
||||
AMS_ASSERT(v.IsLinked());
|
||||
return const_iterator(std::addressof(v));
|
||||
}
|
||||
|
||||
/* Content management. */
|
||||
constexpr ALWAYS_INLINE bool empty() const {
|
||||
return !m_root_node.IsLinked();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_type size() const {
|
||||
return static_cast<size_type>(std::distance(this->begin(), this->end()));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reference back() {
|
||||
return *m_root_node.GetPrev();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_reference back() const {
|
||||
return *m_root_node.GetPrev();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reference front() {
|
||||
return *m_root_node.GetNext();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_reference front() const {
|
||||
return *m_root_node.GetNext();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void push_back(reference node) {
|
||||
m_root_node.LinkPrev(std::addressof(node));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void push_front(reference node) {
|
||||
m_root_node.LinkNext(std::addressof(node));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void pop_back() {
|
||||
m_root_node.GetPrev()->Unlink();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void pop_front() {
|
||||
m_root_node.GetNext()->Unlink();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator insert(const_iterator pos, reference node) {
|
||||
pos.GetNonConstIterator()->LinkPrev(std::addressof(node));
|
||||
return iterator(std::addressof(node));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void splice(const_iterator pos, IntrusiveListImpl &o) {
|
||||
splice_impl(pos, o.begin(), o.end());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void splice(const_iterator pos, IntrusiveListImpl &o, const_iterator first) {
|
||||
AMS_UNUSED(o);
|
||||
const_iterator last(first);
|
||||
std::advance(last, 1);
|
||||
splice_impl(pos, first, last);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void splice(const_iterator pos, IntrusiveListImpl &o, const_iterator first, const_iterator last) {
|
||||
AMS_UNUSED(o);
|
||||
splice_impl(pos, first, last);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator erase(const_iterator pos) {
|
||||
if (pos == this->end()) {
|
||||
return this->end();
|
||||
}
|
||||
iterator it(pos.GetNonConstIterator());
|
||||
(it++)->Unlink();
|
||||
return it;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void clear() {
|
||||
while (!this->empty()) {
|
||||
this->pop_front();
|
||||
}
|
||||
}
|
||||
private:
|
||||
constexpr ALWAYS_INLINE void splice_impl(const_iterator _pos, const_iterator _first, const_iterator _last) {
|
||||
if (_first == _last) {
|
||||
return;
|
||||
}
|
||||
iterator pos(_pos.GetNonConstIterator());
|
||||
iterator first(_first.GetNonConstIterator());
|
||||
iterator last(_last.GetNonConstIterator());
|
||||
first->Unlink(std::addressof(*last));
|
||||
pos->SplicePrev(std::addressof(*first), std::addressof(*first));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<class T, class Traits>
|
||||
class IntrusiveList {
|
||||
NON_COPYABLE(IntrusiveList);
|
||||
private:
|
||||
impl::IntrusiveListImpl m_impl;
|
||||
public:
|
||||
template<bool Const>
|
||||
class Iterator;
|
||||
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using const_pointer = const value_type *;
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using iterator = Iterator<false>;
|
||||
using const_iterator = Iterator<true>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
template<bool Const>
|
||||
class Iterator {
|
||||
public:
|
||||
friend class ams::util::IntrusiveList<T, Traits>;
|
||||
|
||||
using ImplIterator = typename std::conditional<Const, ams::util::impl::IntrusiveListImpl::const_iterator, ams::util::impl::IntrusiveListImpl::iterator>::type;
|
||||
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = typename IntrusiveList::value_type;
|
||||
using difference_type = typename IntrusiveList::difference_type;
|
||||
using pointer = typename std::conditional<Const, IntrusiveList::const_pointer, IntrusiveList::pointer>::type;
|
||||
using reference = typename std::conditional<Const, IntrusiveList::const_reference, IntrusiveList::reference>::type;
|
||||
private:
|
||||
ImplIterator m_iterator;
|
||||
private:
|
||||
constexpr explicit ALWAYS_INLINE Iterator(ImplIterator it) : m_iterator(it) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE ImplIterator GetImplIterator() const {
|
||||
return m_iterator;
|
||||
}
|
||||
public:
|
||||
constexpr ALWAYS_INLINE bool operator==(const Iterator &rhs) const {
|
||||
return m_iterator == rhs.m_iterator;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE pointer operator->() const {
|
||||
return std::addressof(Traits::GetParent(*m_iterator));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reference operator*() const {
|
||||
return Traits::GetParent(*m_iterator);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator &operator++() {
|
||||
++m_iterator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator &operator--() {
|
||||
--m_iterator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator operator++(int) {
|
||||
const Iterator it{*this};
|
||||
++m_iterator;
|
||||
return it;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator operator--(int) {
|
||||
const Iterator it{*this};
|
||||
--m_iterator;
|
||||
return it;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE operator Iterator<true>() const {
|
||||
return Iterator<true>(m_iterator);
|
||||
}
|
||||
};
|
||||
private:
|
||||
static constexpr ALWAYS_INLINE IntrusiveListNode &GetNode(reference ref) {
|
||||
return Traits::GetNode(ref);
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveListNode const &GetNode(const_reference ref) {
|
||||
return Traits::GetNode(ref);
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE reference GetParent(IntrusiveListNode &node) {
|
||||
return Traits::GetParent(node);
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE const_reference GetParent(IntrusiveListNode const &node) {
|
||||
return Traits::GetParent(node);
|
||||
}
|
||||
public:
|
||||
constexpr ALWAYS_INLINE IntrusiveList() : m_impl() { /* ... */ }
|
||||
|
||||
/* Iterator accessors. */
|
||||
constexpr ALWAYS_INLINE iterator begin() {
|
||||
return iterator(m_impl.begin());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator begin() const {
|
||||
return const_iterator(m_impl.begin());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator end() {
|
||||
return iterator(m_impl.end());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator end() const {
|
||||
return const_iterator(m_impl.end());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator cbegin() const {
|
||||
return this->begin();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator cend() const {
|
||||
return this->end();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reverse_iterator rbegin() {
|
||||
return reverse_iterator(this->end());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_reverse_iterator rbegin() const {
|
||||
return const_reverse_iterator(this->end());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reverse_iterator rend() {
|
||||
return reverse_iterator(this->begin());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_reverse_iterator rend() const {
|
||||
return const_reverse_iterator(this->begin());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_reverse_iterator crbegin() const {
|
||||
return this->rbegin();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_reverse_iterator crend() const {
|
||||
return this->rend();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator iterator_to(reference v) {
|
||||
return iterator(m_impl.iterator_to(GetNode(v)));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator iterator_to(const_reference v) const {
|
||||
return const_iterator(m_impl.iterator_to(GetNode(v)));
|
||||
}
|
||||
|
||||
/* Content management. */
|
||||
constexpr ALWAYS_INLINE bool empty() const {
|
||||
return m_impl.empty();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_type size() const {
|
||||
return m_impl.size();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reference back() {
|
||||
AMS_ASSERT(!m_impl.empty());
|
||||
return GetParent(m_impl.back());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_reference back() const {
|
||||
AMS_ASSERT(!m_impl.empty());
|
||||
return GetParent(m_impl.back());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reference front() {
|
||||
AMS_ASSERT(!m_impl.empty());
|
||||
return GetParent(m_impl.front());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_reference front() const {
|
||||
AMS_ASSERT(!m_impl.empty());
|
||||
return GetParent(m_impl.front());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void push_back(reference ref) {
|
||||
m_impl.push_back(GetNode(ref));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void push_front(reference ref) {
|
||||
m_impl.push_front(GetNode(ref));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void pop_back() {
|
||||
AMS_ASSERT(!m_impl.empty());
|
||||
m_impl.pop_back();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void pop_front() {
|
||||
AMS_ASSERT(!m_impl.empty());
|
||||
m_impl.pop_front();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator insert(const_iterator pos, reference ref) {
|
||||
return iterator(m_impl.insert(pos.GetImplIterator(), GetNode(ref)));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void splice(const_iterator pos, IntrusiveList &o) {
|
||||
m_impl.splice(pos.GetImplIterator(), o.m_impl);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void splice(const_iterator pos, IntrusiveList &o, const_iterator first) {
|
||||
m_impl.splice(pos.GetImplIterator(), o.m_impl, first.GetImplIterator());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void splice(const_iterator pos, IntrusiveList &o, const_iterator first, const_iterator last) {
|
||||
m_impl.splice(pos.GetImplIterator(), o.m_impl, first.GetImplIterator(), last.GetImplIterator());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator erase(const_iterator pos) {
|
||||
return iterator(m_impl.erase(pos.GetImplIterator()));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void clear() {
|
||||
m_impl.clear();
|
||||
}
|
||||
};
|
||||
|
||||
template<auto T, class Derived = util::impl::GetParentType<T>>
|
||||
class IntrusiveListMemberTraits;
|
||||
|
||||
template<class Parent, IntrusiveListNode Parent::*Member, class Derived>
|
||||
class IntrusiveListMemberTraits<Member, Derived> {
|
||||
public:
|
||||
using ListType = IntrusiveList<Derived, IntrusiveListMemberTraits>;
|
||||
private:
|
||||
friend class IntrusiveList<Derived, IntrusiveListMemberTraits>;
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveListNode &GetNode(Derived &parent) {
|
||||
return parent.*Member;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveListNode const &GetNode(Derived const &parent) {
|
||||
return parent.*Member;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE Derived &GetParent(IntrusiveListNode &node) {
|
||||
return util::GetParentReference<Member, Derived>(std::addressof(node));
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE Derived const &GetParent(IntrusiveListNode const &node) {
|
||||
return util::GetParentReference<Member, Derived>(std::addressof(node));
|
||||
}
|
||||
};
|
||||
|
||||
template<auto T, class Derived = util::impl::GetParentType<T>>
|
||||
class IntrusiveListMemberTraitsByNonConstexprOffsetOf;
|
||||
|
||||
template<class Parent, IntrusiveListNode Parent::*Member, class Derived>
|
||||
class IntrusiveListMemberTraitsByNonConstexprOffsetOf<Member, Derived> {
|
||||
public:
|
||||
using ListType = IntrusiveList<Derived, IntrusiveListMemberTraitsByNonConstexprOffsetOf>;
|
||||
private:
|
||||
friend class IntrusiveList<Derived, IntrusiveListMemberTraitsByNonConstexprOffsetOf>;
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveListNode &GetNode(Derived &parent) {
|
||||
return parent.*Member;
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveListNode const &GetNode(Derived const &parent) {
|
||||
return parent.*Member;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE Derived &GetParent(IntrusiveListNode &node) {
|
||||
return *reinterpret_cast<Derived *>(reinterpret_cast<char *>(std::addressof(node)) - GetOffset());
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE Derived const &GetParent(IntrusiveListNode const &node) {
|
||||
return *reinterpret_cast<const Derived *>(reinterpret_cast<const char *>(std::addressof(node)) - GetOffset());
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE uintptr_t GetOffset() {
|
||||
return reinterpret_cast<uintptr_t>(std::addressof(reinterpret_cast<Derived *>(0)->*Member));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class IntrusiveListBaseNode : public IntrusiveListNode{};
|
||||
|
||||
template<class Derived>
|
||||
class IntrusiveListBaseTraits {
|
||||
public:
|
||||
using ListType = IntrusiveList<Derived, IntrusiveListBaseTraits>;
|
||||
private:
|
||||
friend class IntrusiveList<Derived, IntrusiveListBaseTraits>;
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveListNode &GetNode(Derived &parent) {
|
||||
return static_cast<IntrusiveListNode &>(static_cast<IntrusiveListBaseNode<Derived> &>(parent));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveListNode const &GetNode(Derived const &parent) {
|
||||
return static_cast<const IntrusiveListNode &>(static_cast<const IntrusiveListBaseNode<Derived> &>(parent));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE Derived &GetParent(IntrusiveListNode &node) {
|
||||
return static_cast<Derived &>(static_cast<IntrusiveListBaseNode<Derived> &>(node));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE Derived const &GetParent(IntrusiveListNode const &node) {
|
||||
return static_cast<const Derived &>(static_cast<const IntrusiveListBaseNode<Derived> &>(node));
|
||||
}
|
||||
};
|
||||
|
||||
AMS_PRAGMA_END_OPTIMIZE()
|
||||
|
||||
}
|
||||
@@ -1,579 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_parent_of_member.hpp>
|
||||
#include <vapours/freebsd/tree.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
AMS_PRAGMA_BEGIN_OPTIMIZE("-O3")
|
||||
|
||||
namespace impl {
|
||||
|
||||
class IntrusiveRedBlackTreeImpl;
|
||||
|
||||
}
|
||||
|
||||
#pragma pack(push, 4)
|
||||
struct IntrusiveRedBlackTreeNode {
|
||||
NON_COPYABLE(IntrusiveRedBlackTreeNode);
|
||||
public:
|
||||
using RBEntry = freebsd::RBEntry<IntrusiveRedBlackTreeNode>;
|
||||
private:
|
||||
RBEntry m_entry;
|
||||
public:
|
||||
constexpr explicit ALWAYS_INLINE IntrusiveRedBlackTreeNode(util::ConstantInitializeTag) : m_entry(util::ConstantInitialize) { /* ... */ }
|
||||
explicit ALWAYS_INLINE IntrusiveRedBlackTreeNode() { /* ... */ }
|
||||
|
||||
[[nodiscard]] constexpr ALWAYS_INLINE RBEntry &GetRBEntry() { return m_entry; }
|
||||
[[nodiscard]] constexpr ALWAYS_INLINE const RBEntry &GetRBEntry() const { return m_entry; }
|
||||
|
||||
constexpr ALWAYS_INLINE void SetRBEntry(const RBEntry &entry) { m_entry = entry; }
|
||||
};
|
||||
static_assert(sizeof(IntrusiveRedBlackTreeNode) == 3 * sizeof(void *) + std::max<size_t>(sizeof(freebsd::RBColor), 4));
|
||||
#pragma pack(pop)
|
||||
|
||||
template<class T, class Traits, class Comparator>
|
||||
class IntrusiveRedBlackTree;
|
||||
|
||||
namespace impl {
|
||||
|
||||
class IntrusiveRedBlackTreeImpl {
|
||||
NON_COPYABLE(IntrusiveRedBlackTreeImpl);
|
||||
private:
|
||||
template<class, class, class>
|
||||
friend class ::ams::util::IntrusiveRedBlackTree;
|
||||
private:
|
||||
using RootType = freebsd::RBHead<IntrusiveRedBlackTreeNode>;
|
||||
private:
|
||||
RootType m_root;
|
||||
public:
|
||||
template<bool Const>
|
||||
class Iterator;
|
||||
|
||||
using value_type = IntrusiveRedBlackTreeNode;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using const_pointer = const value_type *;
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using iterator = Iterator<false>;
|
||||
using const_iterator = Iterator<true>;
|
||||
|
||||
template<bool Const>
|
||||
class Iterator {
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = typename IntrusiveRedBlackTreeImpl::value_type;
|
||||
using difference_type = typename IntrusiveRedBlackTreeImpl::difference_type;
|
||||
using pointer = typename std::conditional<Const, IntrusiveRedBlackTreeImpl::const_pointer, IntrusiveRedBlackTreeImpl::pointer>::type;
|
||||
using reference = typename std::conditional<Const, IntrusiveRedBlackTreeImpl::const_reference, IntrusiveRedBlackTreeImpl::reference>::type;
|
||||
private:
|
||||
pointer m_node;
|
||||
public:
|
||||
constexpr explicit ALWAYS_INLINE Iterator(pointer n) : m_node(n) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE bool operator==(const Iterator &rhs) const {
|
||||
return m_node == rhs.m_node;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE pointer operator->() const {
|
||||
return m_node;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reference operator*() const {
|
||||
return *m_node;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator &operator++() {
|
||||
m_node = GetNext(m_node);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator &operator--() {
|
||||
m_node = GetPrev(m_node);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator operator++(int) {
|
||||
const Iterator it{*this};
|
||||
++(*this);
|
||||
return it;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator operator--(int) {
|
||||
const Iterator it{*this};
|
||||
--(*this);
|
||||
return it;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE operator Iterator<true>() const {
|
||||
return Iterator<true>(m_node);
|
||||
}
|
||||
};
|
||||
private:
|
||||
constexpr ALWAYS_INLINE bool EmptyImpl() const {
|
||||
return m_root.IsEmpty();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode *GetMinImpl() const {
|
||||
return freebsd::RB_MIN(const_cast<RootType &>(m_root));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode *GetMaxImpl() const {
|
||||
return freebsd::RB_MAX(const_cast<RootType &>(m_root));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode *RemoveImpl(IntrusiveRedBlackTreeNode *node) {
|
||||
return freebsd::RB_REMOVE(m_root, node);
|
||||
}
|
||||
public:
|
||||
static constexpr IntrusiveRedBlackTreeNode *GetNext(IntrusiveRedBlackTreeNode *node) {
|
||||
return freebsd::RB_NEXT(node);
|
||||
}
|
||||
|
||||
static constexpr IntrusiveRedBlackTreeNode *GetPrev(IntrusiveRedBlackTreeNode *node) {
|
||||
return freebsd::RB_PREV(node);
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode const *GetNext(IntrusiveRedBlackTreeNode const *node) {
|
||||
return static_cast<const IntrusiveRedBlackTreeNode *>(GetNext(const_cast<IntrusiveRedBlackTreeNode *>(node)));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode const *GetPrev(IntrusiveRedBlackTreeNode const *node) {
|
||||
return static_cast<const IntrusiveRedBlackTreeNode *>(GetPrev(const_cast<IntrusiveRedBlackTreeNode *>(node)));
|
||||
}
|
||||
public:
|
||||
constexpr ALWAYS_INLINE IntrusiveRedBlackTreeImpl() = default;
|
||||
|
||||
/* Iterator accessors. */
|
||||
constexpr ALWAYS_INLINE iterator begin() {
|
||||
return iterator(this->GetMinImpl());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator begin() const {
|
||||
return const_iterator(this->GetMinImpl());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator end() {
|
||||
return iterator(static_cast<IntrusiveRedBlackTreeNode *>(nullptr));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator end() const {
|
||||
return const_iterator(static_cast<const IntrusiveRedBlackTreeNode *>(nullptr));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator cbegin() const {
|
||||
return this->begin();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator cend() const {
|
||||
return this->end();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator iterator_to(reference ref) {
|
||||
return iterator(std::addressof(ref));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator iterator_to(const_reference ref) const {
|
||||
return const_iterator(std::addressof(ref));
|
||||
}
|
||||
|
||||
/* Content management. */
|
||||
constexpr ALWAYS_INLINE bool empty() const {
|
||||
return this->EmptyImpl();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reference back() {
|
||||
return *this->GetMaxImpl();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_reference back() const {
|
||||
return *this->GetMaxImpl();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reference front() {
|
||||
return *this->GetMinImpl();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_reference front() const {
|
||||
return *this->GetMinImpl();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator erase(iterator it) {
|
||||
auto cur = std::addressof(*it);
|
||||
auto next = GetNext(cur);
|
||||
this->RemoveImpl(cur);
|
||||
return iterator(next);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
concept HasRedBlackKeyType = requires {
|
||||
{ std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T, typename Default>
|
||||
consteval auto *GetRedBlackKeyType() {
|
||||
if constexpr (HasRedBlackKeyType<T>) {
|
||||
return static_cast<typename T::RedBlackKeyType *>(nullptr);
|
||||
} else {
|
||||
return static_cast<Default *>(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename T, typename Default>
|
||||
using RedBlackKeyType = typename std::remove_pointer<decltype(impl::GetRedBlackKeyType<T, Default>())>::type;
|
||||
|
||||
template<class T, class Traits, class Comparator>
|
||||
class IntrusiveRedBlackTree {
|
||||
NON_COPYABLE(IntrusiveRedBlackTree);
|
||||
public:
|
||||
using ImplType = impl::IntrusiveRedBlackTreeImpl;
|
||||
private:
|
||||
ImplType m_impl;
|
||||
public:
|
||||
template<bool Const>
|
||||
class Iterator;
|
||||
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
using reference = T &;
|
||||
using const_reference = const T &;
|
||||
using iterator = Iterator<false>;
|
||||
using const_iterator = Iterator<true>;
|
||||
|
||||
using key_type = RedBlackKeyType<Comparator, value_type>;
|
||||
using const_key_pointer = const key_type *;
|
||||
using const_key_reference = const key_type &;
|
||||
|
||||
template<bool Const>
|
||||
class Iterator {
|
||||
public:
|
||||
friend class IntrusiveRedBlackTree<T, Traits, Comparator>;
|
||||
|
||||
using ImplIterator = typename std::conditional<Const, ImplType::const_iterator, ImplType::iterator>::type;
|
||||
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = typename IntrusiveRedBlackTree::value_type;
|
||||
using difference_type = typename IntrusiveRedBlackTree::difference_type;
|
||||
using pointer = typename std::conditional<Const, IntrusiveRedBlackTree::const_pointer, IntrusiveRedBlackTree::pointer>::type;
|
||||
using reference = typename std::conditional<Const, IntrusiveRedBlackTree::const_reference, IntrusiveRedBlackTree::reference>::type;
|
||||
private:
|
||||
ImplIterator m_impl;
|
||||
private:
|
||||
constexpr explicit ALWAYS_INLINE Iterator(ImplIterator it) : m_impl(it) { /* ... */ }
|
||||
|
||||
constexpr explicit ALWAYS_INLINE Iterator(typename ImplIterator::pointer p) : m_impl(p) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE ImplIterator GetImplIterator() const {
|
||||
return m_impl;
|
||||
}
|
||||
public:
|
||||
constexpr ALWAYS_INLINE bool operator==(const Iterator &rhs) const {
|
||||
return m_impl == rhs.m_impl;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE pointer operator->() const {
|
||||
return Traits::GetParent(std::addressof(*m_impl));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reference operator*() const {
|
||||
return *Traits::GetParent(std::addressof(*m_impl));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator &operator++() {
|
||||
++m_impl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator &operator--() {
|
||||
--m_impl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator operator++(int) {
|
||||
const Iterator it{*this};
|
||||
++m_impl;
|
||||
return it;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE Iterator operator--(int) {
|
||||
const Iterator it{*this};
|
||||
--m_impl;
|
||||
return it;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE operator Iterator<true>() const {
|
||||
return Iterator<true>(m_impl);
|
||||
}
|
||||
};
|
||||
private:
|
||||
static constexpr ALWAYS_INLINE int CompareImpl(const IntrusiveRedBlackTreeNode *lhs, const IntrusiveRedBlackTreeNode *rhs) {
|
||||
return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE int CompareKeyImpl(const_key_reference key, const IntrusiveRedBlackTreeNode *rhs) {
|
||||
return Comparator::Compare(key, *Traits::GetParent(rhs));
|
||||
}
|
||||
|
||||
/* Define accessors using RB_* functions. */
|
||||
constexpr IntrusiveRedBlackTreeNode *InsertImpl(IntrusiveRedBlackTreeNode *node) {
|
||||
return freebsd::RB_INSERT(m_impl.m_root, node, CompareImpl);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode *FindImpl(IntrusiveRedBlackTreeNode const *node) const {
|
||||
return freebsd::RB_FIND(const_cast<ImplType::RootType &>(m_impl.m_root), const_cast<IntrusiveRedBlackTreeNode *>(node), CompareImpl);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode *NFindImpl(IntrusiveRedBlackTreeNode const *node) const {
|
||||
return freebsd::RB_NFIND(const_cast<ImplType::RootType &>(m_impl.m_root), const_cast<IntrusiveRedBlackTreeNode *>(node), CompareImpl);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode *FindKeyImpl(const_key_reference key) const {
|
||||
return freebsd::RB_FIND_KEY(const_cast<ImplType::RootType &>(m_impl.m_root), key, CompareKeyImpl);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode *NFindKeyImpl(const_key_reference key) const {
|
||||
return freebsd::RB_NFIND_KEY(const_cast<ImplType::RootType &>(m_impl.m_root), key, CompareKeyImpl);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode *FindExistingImpl(IntrusiveRedBlackTreeNode const *node) const {
|
||||
return freebsd::RB_FIND_EXISTING(const_cast<ImplType::RootType &>(m_impl.m_root), const_cast<IntrusiveRedBlackTreeNode *>(node), CompareImpl);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode *FindExistingKeyImpl(const_key_reference key) const {
|
||||
return freebsd::RB_FIND_EXISTING_KEY(const_cast<ImplType::RootType &>(m_impl.m_root), key, CompareKeyImpl);
|
||||
}
|
||||
public:
|
||||
constexpr ALWAYS_INLINE IntrusiveRedBlackTree() = default;
|
||||
|
||||
/* Iterator accessors. */
|
||||
constexpr ALWAYS_INLINE iterator begin() {
|
||||
return iterator(m_impl.begin());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator begin() const {
|
||||
return const_iterator(m_impl.begin());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator end() {
|
||||
return iterator(m_impl.end());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator end() const {
|
||||
return const_iterator(m_impl.end());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator cbegin() const {
|
||||
return this->begin();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator cend() const {
|
||||
return this->end();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator iterator_to(reference ref) {
|
||||
return iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_iterator iterator_to(const_reference ref) const {
|
||||
return const_iterator(m_impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
|
||||
}
|
||||
|
||||
/* Content management. */
|
||||
constexpr ALWAYS_INLINE bool empty() const {
|
||||
return m_impl.empty();
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reference back() {
|
||||
return *Traits::GetParent(std::addressof(m_impl.back()));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_reference back() const {
|
||||
return *Traits::GetParent(std::addressof(m_impl.back()));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE reference front() {
|
||||
return *Traits::GetParent(std::addressof(m_impl.front()));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const_reference front() const {
|
||||
return *Traits::GetParent(std::addressof(m_impl.front()));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator erase(iterator it) {
|
||||
return iterator(m_impl.erase(it.GetImplIterator()));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator insert(reference ref) {
|
||||
ImplType::pointer node = Traits::GetNode(std::addressof(ref));
|
||||
this->InsertImpl(node);
|
||||
return iterator(node);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator find(const_reference ref) const {
|
||||
return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref))));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator nfind(const_reference ref) const {
|
||||
return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref))));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator find_key(const_key_reference ref) const {
|
||||
return iterator(this->FindKeyImpl(ref));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator nfind_key(const_key_reference ref) const {
|
||||
return iterator(this->NFindKeyImpl(ref));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator find_existing(const_reference ref) const {
|
||||
return iterator(this->FindExistingImpl(Traits::GetNode(std::addressof(ref))));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE iterator find_existing_key(const_key_reference ref) const {
|
||||
return iterator(this->FindExistingKeyImpl(ref));
|
||||
}
|
||||
};
|
||||
|
||||
template<auto T, class Derived = util::impl::GetParentType<T>>
|
||||
class IntrusiveRedBlackTreeMemberTraits;
|
||||
|
||||
template<class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
|
||||
class IntrusiveRedBlackTreeMemberTraits<Member, Derived> {
|
||||
public:
|
||||
template<class Comparator>
|
||||
using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraits, Comparator>;
|
||||
using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
|
||||
private:
|
||||
template<class, class, class>
|
||||
friend class IntrusiveRedBlackTree;
|
||||
|
||||
friend class impl::IntrusiveRedBlackTreeImpl;
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode *GetNode(Derived *parent) {
|
||||
return std::addressof(parent->*Member);
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode const *GetNode(Derived const *parent) {
|
||||
return std::addressof(parent->*Member);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE Derived *GetParent(IntrusiveRedBlackTreeNode *node) {
|
||||
return util::GetParentPointer<Member, Derived>(node);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE Derived const *GetParent(IntrusiveRedBlackTreeNode const *node) {
|
||||
return util::GetParentPointer<Member, Derived>(node);
|
||||
}
|
||||
private:
|
||||
static_assert(util::IsAligned(util::impl::OffsetOf<Member, Derived>::value, alignof(void *)));
|
||||
};
|
||||
|
||||
template<auto T, class Derived = util::impl::GetParentType<T>>
|
||||
class IntrusiveRedBlackTreeMemberTraitsDeferredAssert;
|
||||
|
||||
template<class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
|
||||
class IntrusiveRedBlackTreeMemberTraitsDeferredAssert<Member, Derived> {
|
||||
public:
|
||||
template<class Comparator>
|
||||
using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>;
|
||||
using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
|
||||
|
||||
static constexpr bool IsValid() {
|
||||
return util::IsAligned(util::impl::OffsetOf<Member, Derived>::value, alignof(void *));
|
||||
}
|
||||
private:
|
||||
template<class, class, class>
|
||||
friend class IntrusiveRedBlackTree;
|
||||
|
||||
friend class impl::IntrusiveRedBlackTreeImpl;
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode *GetNode(Derived *parent) {
|
||||
return std::addressof(parent->*Member);
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode const *GetNode(Derived const *parent) {
|
||||
return std::addressof(parent->*Member);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE Derived *GetParent(IntrusiveRedBlackTreeNode *node) {
|
||||
return util::GetParentPointer<Member, Derived>(node);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE Derived const *GetParent(IntrusiveRedBlackTreeNode const *node) {
|
||||
return util::GetParentPointer<Member, Derived>(node);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class alignas(void *) IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode {
|
||||
public:
|
||||
using IntrusiveRedBlackTreeNode::IntrusiveRedBlackTreeNode;
|
||||
|
||||
constexpr ALWAYS_INLINE Derived *GetPrev() { return static_cast< Derived *>(static_cast< IntrusiveRedBlackTreeBaseNode *>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this))); }
|
||||
constexpr ALWAYS_INLINE const Derived *GetPrev() const { return static_cast<const Derived *>(static_cast<const IntrusiveRedBlackTreeBaseNode *>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this))); }
|
||||
|
||||
constexpr ALWAYS_INLINE Derived *GetNext() { return static_cast< Derived *>(static_cast< IntrusiveRedBlackTreeBaseNode *>(impl::IntrusiveRedBlackTreeImpl::GetNext(this))); }
|
||||
constexpr ALWAYS_INLINE const Derived *GetNext() const { return static_cast<const Derived *>(static_cast<const IntrusiveRedBlackTreeBaseNode *>(impl::IntrusiveRedBlackTreeImpl::GetNext(this))); }
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class IntrusiveRedBlackTreeBaseTraits {
|
||||
public:
|
||||
template<class Comparator>
|
||||
using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeBaseTraits, Comparator>;
|
||||
using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
|
||||
private:
|
||||
template<class, class, class>
|
||||
friend class IntrusiveRedBlackTree;
|
||||
|
||||
friend class impl::IntrusiveRedBlackTreeImpl;
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode *GetNode(Derived *parent) {
|
||||
return static_cast<IntrusiveRedBlackTreeNode *>(static_cast<IntrusiveRedBlackTreeBaseNode<Derived> *>(parent));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE IntrusiveRedBlackTreeNode const *GetNode(Derived const *parent) {
|
||||
return static_cast<const IntrusiveRedBlackTreeNode *>(static_cast<const IntrusiveRedBlackTreeBaseNode<Derived> *>(parent));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE Derived *GetParent(IntrusiveRedBlackTreeNode *node) {
|
||||
return static_cast<Derived *>(static_cast<IntrusiveRedBlackTreeBaseNode<Derived> *>(node));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE Derived const *GetParent(IntrusiveRedBlackTreeNode const *node) {
|
||||
return static_cast<const Derived *>(static_cast<const IntrusiveRedBlackTreeBaseNode<Derived> *>(node));
|
||||
}
|
||||
};
|
||||
|
||||
AMS_PRAGMA_END_OPTIMIZE()
|
||||
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename _Mutex>
|
||||
class unique_lock {
|
||||
NON_COPYABLE(unique_lock);
|
||||
public:
|
||||
using mutex_type = _Mutex;
|
||||
private:
|
||||
mutex_type *m_mutex;
|
||||
bool m_owns;
|
||||
public:
|
||||
unique_lock() noexcept : m_mutex(nullptr), m_owns(false) { /* ... */ }
|
||||
|
||||
explicit unique_lock(mutex_type &m) noexcept : m_mutex(std::addressof(m)), m_owns(false) {
|
||||
this->lock();
|
||||
m_owns = true;
|
||||
}
|
||||
|
||||
unique_lock(mutex_type &m, std::defer_lock_t) noexcept : m_mutex(std::addressof(m)), m_owns(false) { /* ... */ }
|
||||
unique_lock(mutex_type &m, std::try_to_lock_t) noexcept : m_mutex(std::addressof(m)), m_owns(m_mutex->try_lock()) { /* ... */ }
|
||||
unique_lock(mutex_type &m, std::adopt_lock_t) noexcept : m_mutex(std::addressof(m)), m_owns(true) { /* ... */ }
|
||||
|
||||
template<typename _Clock, typename _Duration>
|
||||
unique_lock(mutex_type &m, const std::chrono::time_point<_Clock, _Duration> &time) noexcept : m_mutex(std::addressof(m)), m_owns(m_mutex->try_lock_until(time)) { /* ... */ }
|
||||
|
||||
template<typename _Rep, typename _Period>
|
||||
unique_lock(mutex_type &m, const std::chrono::duration<_Rep, _Period> &time) noexcept : m_mutex(std::addressof(m)), m_owns(m_mutex->try_lock_for(time)) { /* ... */ }
|
||||
|
||||
~unique_lock() noexcept {
|
||||
if (m_owns) {
|
||||
this->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
unique_lock(unique_lock &&rhs) noexcept : m_mutex(rhs.m_mutex), m_owns(rhs.m_owns) {
|
||||
rhs.m_mutex = nullptr;
|
||||
rhs.m_owns = false;
|
||||
}
|
||||
|
||||
unique_lock &operator=(unique_lock &&rhs) noexcept {
|
||||
if (m_owns) {
|
||||
this->unlock();
|
||||
}
|
||||
|
||||
unique_lock(std::move(rhs)).swap(*this);
|
||||
|
||||
rhs.m_mutex = nullptr;
|
||||
rhs.m_owns = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void lock() noexcept {
|
||||
AMS_ABORT_UNLESS(m_mutex);
|
||||
AMS_ABORT_UNLESS(!m_owns);
|
||||
|
||||
m_mutex->lock();
|
||||
m_owns = true;
|
||||
}
|
||||
|
||||
bool try_lock() noexcept {
|
||||
AMS_ABORT_UNLESS(m_mutex);
|
||||
AMS_ABORT_UNLESS(!m_owns);
|
||||
|
||||
m_owns = m_mutex->try_lock();
|
||||
return m_owns;
|
||||
}
|
||||
|
||||
template<typename _Clock, typename _Duration>
|
||||
bool try_lock_until(const std::chrono::time_point<_Clock, _Duration> &time) noexcept {
|
||||
AMS_ABORT_UNLESS(m_mutex);
|
||||
AMS_ABORT_UNLESS(!m_owns);
|
||||
m_owns = m_mutex->try_lock_until(time);
|
||||
return m_owns;
|
||||
}
|
||||
|
||||
template<typename _Rep, typename _Period>
|
||||
bool try_lock_for(const std::chrono::duration<_Rep, _Period> &time) noexcept {
|
||||
AMS_ABORT_UNLESS(m_mutex);
|
||||
AMS_ABORT_UNLESS(!m_owns);
|
||||
m_owns = m_mutex->try_lock_for(time);
|
||||
return m_owns;
|
||||
}
|
||||
|
||||
|
||||
void unlock() noexcept {
|
||||
AMS_ABORT_UNLESS(m_owns);
|
||||
if (m_mutex) {
|
||||
m_mutex->unlock();
|
||||
m_owns = false;
|
||||
}
|
||||
}
|
||||
|
||||
void swap(unique_lock &rhs) noexcept {
|
||||
std::swap(m_mutex, rhs.m_mutex);
|
||||
std::swap(m_owns, rhs.m_owns);
|
||||
}
|
||||
|
||||
mutex_type *release() noexcept {
|
||||
mutex_type *ret = m_mutex;
|
||||
m_mutex = nullptr;
|
||||
m_owns = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool owns_lock() const noexcept {
|
||||
return m_owns;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return this->owns_lock();
|
||||
}
|
||||
|
||||
mutex_type *mutex() const noexcept {
|
||||
return m_mutex;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename _Mutex>
|
||||
inline void swap(unique_lock<_Mutex> &lhs, unique_lock<_Mutex> &rhs) noexcept { return lhs.swap(rhs); }
|
||||
|
||||
}
|
||||
@@ -1,719 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_in_place.hpp>
|
||||
#include <vapours/util/impl/util_enable_copy_move.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace impl {
|
||||
|
||||
class NulloptHelper {
|
||||
public:
|
||||
template<typename T>
|
||||
static consteval T CreateInstance() {
|
||||
return T(T::ConstructionArgument::Token);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
struct OptionalFunction {
|
||||
F &m_f;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
struct nullopt_t {
|
||||
private:
|
||||
friend class impl::NulloptHelper;
|
||||
|
||||
enum class ConstructionArgument {
|
||||
Token,
|
||||
};
|
||||
public:
|
||||
consteval nullopt_t(ConstructionArgument) { /* ... */ }
|
||||
};
|
||||
|
||||
constexpr inline nullopt_t nullopt = impl::NulloptHelper::CreateInstance<nullopt_t>();
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T>
|
||||
struct OptionalPayloadBase {
|
||||
using StoredType = typename std::remove_const<T>::type;
|
||||
|
||||
struct EmptyType{};
|
||||
|
||||
template<typename U, bool = std::is_trivially_destructible<U>::value>
|
||||
union StorageType {
|
||||
EmptyType m_empty;
|
||||
U m_value;
|
||||
|
||||
constexpr ALWAYS_INLINE StorageType() : m_empty() { /* ... */ }
|
||||
|
||||
template<typename... Args>
|
||||
constexpr ALWAYS_INLINE StorageType(in_place_t, Args &&... args) : m_value(std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
template<typename V, typename... Args>
|
||||
constexpr ALWAYS_INLINE StorageType(std::initializer_list<V> il, Args &&... args) : m_value(il, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
template<typename F, typename Arg>
|
||||
constexpr ALWAYS_INLINE StorageType(OptionalFunction<F> f, Arg &&arg) : m_value(std::invoke(std::forward<F>(f.m_f), std::forward<Arg>(arg))) { /* ... */ }
|
||||
};
|
||||
|
||||
template<typename U>
|
||||
union StorageType<U, false> {
|
||||
EmptyType m_empty;
|
||||
U m_value;
|
||||
|
||||
constexpr ALWAYS_INLINE StorageType() : m_empty() { /* ... */ }
|
||||
|
||||
template<typename... Args>
|
||||
constexpr ALWAYS_INLINE StorageType(in_place_t, Args &&... args) : m_value(std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
template<typename V, typename... Args>
|
||||
constexpr ALWAYS_INLINE StorageType(std::initializer_list<V> il, Args &&... args) : m_value(il, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
template<typename F, typename Arg>
|
||||
constexpr ALWAYS_INLINE StorageType(OptionalFunction<F> f, Arg &&arg) : m_value(std::invoke(std::forward<F>(f.m_f), std::forward<Arg>(arg))) { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE ~StorageType() { /* ... */ }
|
||||
};
|
||||
|
||||
StorageType<StoredType> m_payload;
|
||||
bool m_engaged = false;
|
||||
|
||||
constexpr OptionalPayloadBase() = default;
|
||||
constexpr ~OptionalPayloadBase() = default;
|
||||
|
||||
template<typename... Args>
|
||||
constexpr OptionalPayloadBase(in_place_t tag, Args &&... args) : m_payload(tag, std::forward<Args>(args)...), m_engaged(true) { /* ... */ }
|
||||
|
||||
template<typename U, typename... Args>
|
||||
constexpr OptionalPayloadBase(std::initializer_list<U> il, Args &&... args) : m_payload(il, std::forward<Args>(args)...), m_engaged(true) { /* ... */ }
|
||||
|
||||
constexpr OptionalPayloadBase(bool engaged, const OptionalPayloadBase &rhs) { AMS_UNUSED(engaged); if (rhs.m_engaged) { this->Construct(rhs.Get()); } }
|
||||
constexpr OptionalPayloadBase(bool engaged, OptionalPayloadBase &&rhs) { AMS_UNUSED(engaged); if (rhs.m_engaged) { this->Construct(std::move(rhs.Get())); } }
|
||||
|
||||
constexpr OptionalPayloadBase(const OptionalPayloadBase &) = default;
|
||||
constexpr OptionalPayloadBase(OptionalPayloadBase &&) = default;
|
||||
|
||||
constexpr OptionalPayloadBase &operator=(const OptionalPayloadBase &) = default;
|
||||
constexpr OptionalPayloadBase &operator=(OptionalPayloadBase &&) = default;
|
||||
|
||||
constexpr void CopyAssign(const OptionalPayloadBase &rhs) {
|
||||
if (m_engaged && rhs.m_engaged) {
|
||||
this->Get() = rhs.Get();
|
||||
} else if (rhs.m_engaged) {
|
||||
this->Construct(rhs.Get());
|
||||
} else {
|
||||
this->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void MoveAssign(OptionalPayloadBase &&rhs) {
|
||||
if (m_engaged && rhs.m_engaged) {
|
||||
this->Get() = std::move(rhs.Get());
|
||||
} else if (rhs.m_engaged) {
|
||||
this->Construct(std::move(rhs.Get()));
|
||||
} else {
|
||||
this->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
constexpr void Construct(Args &&... args) {
|
||||
std::construct_at(std::addressof(m_payload.m_value), std::forward<Args>(args)...);
|
||||
m_engaged = true;
|
||||
}
|
||||
|
||||
constexpr void Destroy() {
|
||||
m_engaged = false;
|
||||
std::destroy_at(std::addressof(m_payload.m_value));
|
||||
}
|
||||
|
||||
template<typename F, typename Arg>
|
||||
constexpr void Apply(impl::OptionalFunction<F> f, Arg &&arg) {
|
||||
std::construct_at(std::addressof(m_payload), f, std::forward<Arg>(arg));
|
||||
m_engaged = true;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE T &Get() { return m_payload.m_value; }
|
||||
constexpr ALWAYS_INLINE const T &Get() const { return m_payload.m_value; }
|
||||
|
||||
constexpr void Reset() {
|
||||
if (m_engaged) {
|
||||
this->Destroy();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, bool = std::is_trivially_destructible<T>::value, bool = std::is_trivially_copy_assignable<T>::value && std::is_trivially_copy_constructible<T>::value, bool = std::is_trivially_move_assignable<T>::value && std::is_trivially_move_constructible<T>::value>
|
||||
struct OptionalPayload;
|
||||
|
||||
template<typename T>
|
||||
struct OptionalPayload<T, true, true, true> : OptionalPayloadBase<T> {
|
||||
using OptionalPayloadBase<T>::OptionalPayloadBase;
|
||||
|
||||
constexpr OptionalPayload() = default;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct OptionalPayload<T, true, false, true> : OptionalPayloadBase<T> {
|
||||
using OptionalPayloadBase<T>::OptionalPayloadBase;
|
||||
|
||||
constexpr OptionalPayload() = default;
|
||||
constexpr ~OptionalPayload() = default;
|
||||
constexpr OptionalPayload(const OptionalPayload &) = default;
|
||||
constexpr OptionalPayload(OptionalPayload &&) = default;
|
||||
constexpr OptionalPayload& operator=(OptionalPayload &&) = default;
|
||||
|
||||
constexpr OptionalPayload &operator=(const OptionalPayload &rhs) {
|
||||
this->CopyAssign(rhs);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct OptionalPayload<T, true, true, false> : OptionalPayloadBase<T> {
|
||||
using OptionalPayloadBase<T>::OptionalPayloadBase;
|
||||
|
||||
constexpr OptionalPayload() = default;
|
||||
constexpr ~OptionalPayload() = default;
|
||||
constexpr OptionalPayload(const OptionalPayload &) = default;
|
||||
constexpr OptionalPayload(OptionalPayload &&) = default;
|
||||
constexpr OptionalPayload& operator=(const OptionalPayload &) = default;
|
||||
|
||||
constexpr OptionalPayload &operator=(OptionalPayload &&rhs) {
|
||||
this->MoveAssign(std::move(rhs));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct OptionalPayload<T, true, false, false> : OptionalPayloadBase<T> {
|
||||
using OptionalPayloadBase<T>::OptionalPayloadBase;
|
||||
|
||||
constexpr OptionalPayload() = default;
|
||||
constexpr ~OptionalPayload() = default;
|
||||
constexpr OptionalPayload(const OptionalPayload &) = default;
|
||||
constexpr OptionalPayload(OptionalPayload &&) = default;
|
||||
|
||||
constexpr OptionalPayload &operator=(const OptionalPayload &rhs) {
|
||||
this->CopyAssign(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr OptionalPayload &operator=(OptionalPayload &&rhs) {
|
||||
this->MoveAssign(std::move(rhs));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, bool TrivialCopy, bool TrivialMove>
|
||||
struct OptionalPayload<T, false, TrivialCopy, TrivialMove> : OptionalPayload<T, true, TrivialCopy, TrivialMove> {
|
||||
using OptionalPayload<T, true, TrivialCopy, TrivialMove>::OptionalPayload;
|
||||
|
||||
constexpr OptionalPayload() = default;
|
||||
constexpr OptionalPayload(const OptionalPayload &) = default;
|
||||
constexpr OptionalPayload(OptionalPayload &&) = default;
|
||||
constexpr OptionalPayload& operator=(const OptionalPayload &) = default;
|
||||
constexpr OptionalPayload& operator=(OptionalPayload &&) = default;
|
||||
|
||||
constexpr ~OptionalPayload() { this->Reset(); }
|
||||
};
|
||||
|
||||
template<typename T, typename Derived>
|
||||
class OptionalBaseImpl {
|
||||
protected:
|
||||
using StoredType = std::remove_const_t<T>;
|
||||
|
||||
template<typename... Args>
|
||||
constexpr void ConstructImpl(Args &&... args) { static_cast<Derived *>(this)->m_payload.Construct(std::forward<Args>(args)...); }
|
||||
|
||||
constexpr void DestructImpl() { static_cast<Derived *>(this)->m_payload.Destroy(); }
|
||||
|
||||
constexpr void ResetImpl() { static_cast<Derived *>(this)->m_payload.Reset(); }
|
||||
|
||||
template<typename F, typename Arg>
|
||||
constexpr void ApplyImpl(OptionalFunction<F> f, Arg &&arg) {
|
||||
static_cast<Derived *>(this)->m_payload.Apply(f, std::forward<Arg>(arg));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsEngagedImpl() const { return static_cast<const Derived *>(this)->m_payload.m_engaged; }
|
||||
|
||||
constexpr ALWAYS_INLINE T &GetImpl() { return static_cast<Derived *>(this)->m_payload.Get(); }
|
||||
constexpr ALWAYS_INLINE const T &GetImpl() const { return static_cast<const Derived *>(this)->m_payload.Get(); }
|
||||
};
|
||||
|
||||
template<typename T, bool = std::is_trivially_copy_constructible<T>::value, bool = std::is_trivially_move_constructible<T>::value>
|
||||
struct OptionalBase : OptionalBaseImpl<T, OptionalBase<T>> {
|
||||
OptionalPayload<T> m_payload;
|
||||
|
||||
constexpr OptionalBase() = default;
|
||||
|
||||
template<typename... Args, std::enable_if_t<std::is_constructible<T, Args...>::value, bool> = false>
|
||||
constexpr explicit OptionalBase(in_place_t, Args &&... args) : m_payload(in_place, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
template<typename U, typename... Args, std::enable_if_t<std::is_constructible<T, std::initializer_list<U> &, Args...>::value, bool> = false>
|
||||
constexpr explicit OptionalBase(in_place_t, std::initializer_list<U> il, Args &&... args) : m_payload(in_place, il, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
constexpr OptionalBase(const OptionalBase &rhs) : m_payload(rhs.m_payload.m_engaged, rhs.m_payload) { /* ... */ }
|
||||
constexpr OptionalBase(OptionalBase &&rhs) : m_payload(rhs.m_payload.m_engaged, std::move(rhs.m_payload)) { /* ... */ }
|
||||
|
||||
constexpr OptionalBase &operator=(const OptionalBase &) = default;
|
||||
constexpr OptionalBase &operator=(OptionalBase &&) = default;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct OptionalBase<T, false, true> : OptionalBaseImpl<T, OptionalBase<T>> {
|
||||
OptionalPayload<T> m_payload;
|
||||
|
||||
constexpr OptionalBase() = default;
|
||||
|
||||
template<typename... Args, std::enable_if_t<std::is_constructible<T, Args...>::value, bool> = false>
|
||||
constexpr explicit OptionalBase(in_place_t, Args &&... args) : m_payload(in_place, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
template<typename U, typename... Args, std::enable_if_t<std::is_constructible<T, std::initializer_list<U> &, Args...>::value, bool> = false>
|
||||
constexpr explicit OptionalBase(in_place_t, std::initializer_list<U> il, Args &&... args) : m_payload(in_place, il, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
constexpr OptionalBase(const OptionalBase &rhs) : m_payload(rhs.m_payload.m_engaged, rhs.m_payload) { /* ... */ }
|
||||
constexpr OptionalBase(OptionalBase &&rhs) = default;
|
||||
|
||||
constexpr OptionalBase &operator=(const OptionalBase &) = default;
|
||||
constexpr OptionalBase &operator=(OptionalBase &&) = default;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct OptionalBase<T, true, false> : OptionalBaseImpl<T, OptionalBase<T>> {
|
||||
OptionalPayload<T> m_payload;
|
||||
|
||||
constexpr OptionalBase() = default;
|
||||
|
||||
template<typename... Args, std::enable_if_t<std::is_constructible<T, Args...>::value, bool> = false>
|
||||
constexpr explicit OptionalBase(in_place_t, Args &&... args) : m_payload(in_place, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
template<typename U, typename... Args, std::enable_if_t<std::is_constructible<T, std::initializer_list<U> &, Args...>::value, bool> = false>
|
||||
constexpr explicit OptionalBase(in_place_t, std::initializer_list<U> il, Args &&... args) : m_payload(in_place, il, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
constexpr OptionalBase(const OptionalBase &rhs) = default;
|
||||
constexpr OptionalBase(OptionalBase &&rhs) : m_payload(rhs.m_payload.m_engaged, std::move(rhs.m_payload)) { /* ... */ }
|
||||
|
||||
constexpr OptionalBase &operator=(const OptionalBase &) = default;
|
||||
constexpr OptionalBase &operator=(OptionalBase &&) = default;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct OptionalBase<T, true, true> : OptionalBaseImpl<T, OptionalBase<T>> {
|
||||
OptionalPayload<T> m_payload;
|
||||
|
||||
constexpr OptionalBase() = default;
|
||||
|
||||
template<typename... Args, std::enable_if_t<std::is_constructible<T, Args...>::value, bool> = false>
|
||||
constexpr explicit OptionalBase(in_place_t, Args &&... args) : m_payload(in_place, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
template<typename U, typename... Args, std::enable_if_t<std::is_constructible<T, std::initializer_list<U> &, Args...>::value, bool> = false>
|
||||
constexpr explicit OptionalBase(in_place_t, std::initializer_list<U> il, Args &&... args) : m_payload(in_place, il, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
constexpr OptionalBase(const OptionalBase &rhs) = default;
|
||||
constexpr OptionalBase(OptionalBase &&rhs) = default;
|
||||
|
||||
constexpr OptionalBase &operator=(const OptionalBase &) = default;
|
||||
constexpr OptionalBase &operator=(OptionalBase &&) = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class optional;
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline bool ConvertsFromOptional = std::is_constructible<T, const optional<U> &>::value ||
|
||||
std::is_constructible<T, optional<U> &>::value ||
|
||||
std::is_constructible<T, const optional<U> &&>::value ||
|
||||
std::is_constructible<T, optional<U> &&>::value ||
|
||||
std::is_convertible<const optional<U> &, T>::value ||
|
||||
std::is_convertible<optional<U> &, T>::value ||
|
||||
std::is_convertible<const optional<U> &&, T>::value ||
|
||||
std::is_convertible<optional<U> &&, T>::value;
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline bool AssignsFromOptional = std::is_assignable<T &, const optional<U> &>::value ||
|
||||
std::is_assignable<T &, optional<U> &>::value ||
|
||||
std::is_assignable<T &, const optional<U> &&>::value ||
|
||||
std::is_assignable<T &, optional<U> &&>::value;
|
||||
template<typename T>
|
||||
constexpr inline bool IsOptional = false;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsOptional<optional<T>> = true;
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class optional : private impl::OptionalBase<T>, private impl::EnableCopyMove<std::is_copy_constructible<T>::value, std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value, std::is_move_constructible<T>::value, std::is_move_constructible<T>::value && std::is_move_assignable<T>::value, optional<T>> {
|
||||
static_assert(!std::is_same<std::remove_cv_t<T>, ::ams::util::nullopt_t>::value);
|
||||
static_assert(!std::is_same<std::remove_cv_t<T>, ::ams::util::in_place_t>::value);
|
||||
static_assert(!std::is_reference<T>::value);
|
||||
private:
|
||||
using Base = impl::OptionalBase<T>;
|
||||
|
||||
template<typename U> static constexpr inline bool IsNotSelf = !std::is_same<optional, std::remove_cvref_t<U>>::value;
|
||||
template<typename U> static constexpr inline bool IsNotTag = !std::is_same<::ams::util::in_place_t, std::remove_cvref_t<U>>::value && !std::is_same<::std::in_place_t, std::remove_cvref_t<U>>::value;
|
||||
|
||||
template<bool... Cond>
|
||||
using Requires = std::enable_if_t<(Cond && ...), bool>;
|
||||
public:
|
||||
using value_type = T;
|
||||
public:
|
||||
constexpr optional() { /* ... */ }
|
||||
constexpr optional(nullopt_t) { /* ... */ }
|
||||
|
||||
template<typename U = T, Requires<IsNotSelf<U>, IsNotTag<U>, std::is_constructible<T, U>::value, std::is_convertible<U, T>::value> = true>
|
||||
constexpr optional(U &&u) : Base(::ams::util::in_place, std::forward<U>(u)) { /* ... */ }
|
||||
|
||||
template<typename U = T, Requires<IsNotSelf<U>, IsNotTag<U>, std::is_constructible<T, U>::value, !std::is_convertible<U, T>::value> = false>
|
||||
constexpr explicit optional(U &&u) : Base(::ams::util::in_place, std::forward<U>(u)) { /* ... */ }
|
||||
|
||||
template<typename U, Requires<!std::is_same<T, U>::value, std::is_constructible<T, const U &>::value, std::is_convertible<const U &, T>::value, !impl::ConvertsFromOptional<T, U>> = true>
|
||||
constexpr optional(const optional<U> &u) {
|
||||
if (u) {
|
||||
this->emplace(*u);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U, Requires<!std::is_same<T, U>::value, std::is_constructible<T, const U &>::value, !std::is_convertible<const U &, T>::value, !impl::ConvertsFromOptional<T, U>> = false>
|
||||
constexpr explicit optional(const optional<U> &u) {
|
||||
if (u) {
|
||||
this->emplace(*u);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U, Requires<!std::is_same<T, U>::value, std::is_constructible<T, const U &>::value, std::is_convertible<const U &, T>::value, !impl::ConvertsFromOptional<T, U>> = true>
|
||||
constexpr optional(optional<U> &&u) {
|
||||
if (u) {
|
||||
this->emplace(std::move(*u));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U, Requires<!std::is_same<T, U>::value, std::is_constructible<T, const U &>::value, !std::is_convertible<const U &, T>::value, !impl::ConvertsFromOptional<T, U>> = false>
|
||||
constexpr explicit optional(optional<U> &&u) {
|
||||
if (u) {
|
||||
this->emplace(std::move(*u));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args, Requires<std::is_constructible<T, Args...>::value> = false>
|
||||
constexpr explicit optional(in_place_t, Args &&... args) : Base(::ams::util::in_place, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
template<typename U, typename... Args, Requires<std::is_constructible<T, std::initializer_list<U> &, Args...>::value> = false>
|
||||
constexpr explicit optional(in_place_t, std::initializer_list<U> il, Args &&... args) : Base(::ams::util::in_place, il, std::forward<Args>(args)...) { /* ... */ }
|
||||
|
||||
constexpr optional &operator=(nullopt_t) { this->ResetImpl(); return *this; }
|
||||
|
||||
template<typename U = T>
|
||||
constexpr std::enable_if_t<IsNotSelf<U> && !(std::is_scalar<T>::value && std::is_same<T, std::decay_t<U>>::value) && std::is_constructible<T, U>::value && std::is_assignable<T &, U>::value,
|
||||
optional &>
|
||||
operator =(U &&u) {
|
||||
if (this->IsEngagedImpl()) {
|
||||
this->GetImpl() = std::forward<U>(u);
|
||||
} else {
|
||||
this->ConstructImpl(std::forward<U>(u));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr std::enable_if_t<!std::is_same<T, U>::value && std::is_constructible<T, const U &>::value && std::is_assignable<T &, const U &>::value && !impl::ConvertsFromOptional<T, U> && !impl::AssignsFromOptional<T, U>,
|
||||
optional &>
|
||||
operator =(const optional<U> &u) {
|
||||
if (u) {
|
||||
if (this->IsEngagedImpl()) {
|
||||
this->GetImpl() = *u;
|
||||
} else {
|
||||
this->ConstructImpl(*u);
|
||||
}
|
||||
} else {
|
||||
this->ResetImpl();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr std::enable_if_t<!std::is_same<T, U>::value && std::is_constructible<T, U>::value && std::is_assignable<T &, U>::value && !impl::ConvertsFromOptional<T, U> && !impl::AssignsFromOptional<T, U>,
|
||||
optional &>
|
||||
operator =(optional<U> &&u) {
|
||||
if (u) {
|
||||
if (this->IsEngagedImpl()) {
|
||||
this->GetImpl() = std::move(*u);
|
||||
} else {
|
||||
this->ConstructImpl(std::move(*u));
|
||||
}
|
||||
} else {
|
||||
this->ResetImpl();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
constexpr std::enable_if_t<std::is_constructible<T, Args...>::value, T &> emplace(Args &&... args) {
|
||||
this->ResetImpl();
|
||||
this->ConstructImpl(std::forward<Args>(args)...);
|
||||
return this->GetImpl();
|
||||
}
|
||||
|
||||
template<typename U, typename... Args>
|
||||
constexpr std::enable_if_t<std::is_constructible<T, std::initializer_list<U> &, Args...>::value, T &> emplace(std::initializer_list<U> il, Args &&... args) {
|
||||
this->ResetImpl();
|
||||
this->ConstructImpl(il, std::forward<Args>(args)...);
|
||||
return this->GetImpl();
|
||||
}
|
||||
|
||||
constexpr void swap(optional &rhs) {
|
||||
if (this->IsEngagedImpl() && rhs.IsEngagedImpl()) {
|
||||
std::swap(this->GetImpl(), rhs.GetImpl());
|
||||
} else if (this->IsEngagedImpl()) {
|
||||
rhs.ConstructImpl(std::move(this->GetImpl()));
|
||||
this->DestructImpl();
|
||||
} else if (rhs.IsEngagedImpl()) {
|
||||
this->ConstructImpl(std::move(rhs.GetImpl()));
|
||||
rhs.DestructImpl();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE const T *operator ->() const { return std::addressof(this->GetImpl()); }
|
||||
constexpr ALWAYS_INLINE T *operator ->() { return std::addressof(this->GetImpl()); }
|
||||
|
||||
constexpr ALWAYS_INLINE const T &operator *() const & { return this->GetImpl(); }
|
||||
constexpr ALWAYS_INLINE T &operator *() & { return this->GetImpl(); }
|
||||
|
||||
constexpr ALWAYS_INLINE const T &&operator *() const && { return std::move(this->GetImpl()); }
|
||||
constexpr ALWAYS_INLINE T &&operator *() && { return std::move(this->GetImpl()); }
|
||||
|
||||
constexpr ALWAYS_INLINE explicit operator bool() const { return this->IsEngagedImpl(); }
|
||||
constexpr ALWAYS_INLINE bool has_value() const { return this->IsEngagedImpl(); }
|
||||
|
||||
constexpr ALWAYS_INLINE const T &value() const & { /* AMS_ASSERT(this->IsEngagedImpl()); */ return this->GetImpl(); }
|
||||
constexpr ALWAYS_INLINE T &value() & { /* AMS_ASSERT(this->IsEngagedImpl()); */ return this->GetImpl(); }
|
||||
|
||||
constexpr ALWAYS_INLINE const T &&value() const && { /* AMS_ASSERT(this->IsEngagedImpl()); */ return std::move(this->GetImpl()); }
|
||||
constexpr ALWAYS_INLINE T &&value() && { /* AMS_ASSERT(this->IsEngagedImpl()); */ return std::move(this->GetImpl()); }
|
||||
|
||||
template<typename U>
|
||||
constexpr T value_or(U &&u) const & {
|
||||
static_assert(std::is_copy_constructible<T>::value);
|
||||
static_assert(std::is_convertible<U &&, T>::value);
|
||||
|
||||
return this->IsEngagedImpl() ? this->GetImpl() : static_cast<T>(std::forward<U>(u));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr T value_or(U &&u) && {
|
||||
static_assert(std::is_move_constructible<T>::value);
|
||||
static_assert(std::is_convertible<U &&, T>::value);
|
||||
|
||||
return this->IsEngagedImpl() ? std::move(this->GetImpl()) : static_cast<T>(std::forward<U>(u));
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto and_then(F &&f) & {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, T &>::type>::type;
|
||||
static_assert(impl::IsOptional<typename std::remove_cvref<U>::type>);
|
||||
return this->IsEngagedImpl() ? std::invoke(std::forward<F>(f), **this) : U{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto and_then(F &&f) const & {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, const T &>::type>::type;
|
||||
static_assert(impl::IsOptional<typename std::remove_cvref<U>::type>);
|
||||
return this->IsEngagedImpl() ? std::invoke(std::forward<F>(f), **this) : U{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto and_then(F &&f) && {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, T>::type>::type;
|
||||
static_assert(impl::IsOptional<typename std::remove_cvref<U>::type>);
|
||||
return this->IsEngagedImpl() ? std::invoke(std::forward<F>(f), std::move(**this)) : U{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto and_then(F &&f) const && {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, const T>::type>::type;
|
||||
static_assert(impl::IsOptional<typename std::remove_cvref<U>::type>);
|
||||
return this->IsEngagedImpl() ? std::invoke(std::forward<F>(f), std::move(**this)) : U{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto transform(F &&f) & {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, T &>::type>::type;
|
||||
return this->IsEngagedImpl() ? optional<U>(impl::OptionalFunction<F>{f}, **this) : optional<U>{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto transform(F &&f) const & {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, const T &>::type>::type;
|
||||
return this->IsEngagedImpl() ? optional<U>(impl::OptionalFunction<F>{f}, **this) : optional<U>{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto transform(F &&f) && {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, T>::type>::type;
|
||||
return this->IsEngagedImpl() ? optional<U>(impl::OptionalFunction<F>{f}, std::move(**this)) : optional<U>{};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
constexpr auto transform(F &&f) const && {
|
||||
using U = typename std::remove_cvref<typename std::invoke_result<F, const T>::type>::type;
|
||||
return this->IsEngagedImpl() ? optional<U>(impl::OptionalFunction<F>{f}, std::move(**this)) : optional<U>{};
|
||||
}
|
||||
|
||||
template<typename F> requires std::invocable<F> && std::copy_constructible<T>
|
||||
constexpr optional or_else(F &&f) const & {
|
||||
using U = typename std::invoke_result<F>::type;
|
||||
static_assert(std::same_as<typename std::remove_cvref_t<U>, optional>);
|
||||
return this->IsEngagedImpl() ? *this : std::forward<F>(f)();
|
||||
}
|
||||
|
||||
template<typename F> requires std::invocable<F> && std::move_constructible<T>
|
||||
constexpr optional or_else(F &&f) && {
|
||||
using U = typename std::invoke_result<F>::type;
|
||||
static_assert(std::same_as<typename std::remove_cvref_t<U>, optional>);
|
||||
return this->IsEngagedImpl() ? std::move(*this) : std::forward<F>(f)();
|
||||
}
|
||||
|
||||
constexpr void reset() { this->ResetImpl(); }
|
||||
private:
|
||||
template<typename U> friend class optional;
|
||||
|
||||
template<typename F, typename Arg>
|
||||
constexpr explicit optional(impl::OptionalFunction<F> f, Arg &&arg) {
|
||||
this->ApplyImpl(f, std::forward<Arg>(arg));
|
||||
}
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T> using optional_relop_t = std::enable_if_t<std::is_convertible<T, bool>::value, bool>;
|
||||
|
||||
template<typename T, typename U> using optional_eq_t = optional_relop_t<decltype(std::declval<const T &>() == std::declval<const U &>())>;
|
||||
template<typename T, typename U> using optional_ne_t = optional_relop_t<decltype(std::declval<const T &>() != std::declval<const U &>())>;
|
||||
template<typename T, typename U> using optional_le_t = optional_relop_t<decltype(std::declval<const T &>() <= std::declval<const U &>())>;
|
||||
template<typename T, typename U> using optional_ge_t = optional_relop_t<decltype(std::declval<const T &>() >= std::declval<const U &>())>;
|
||||
template<typename T, typename U> using optional_lt_t = optional_relop_t<decltype(std::declval<const T &>() < std::declval<const U &>())>;
|
||||
template<typename T, typename U> using optional_gt_t = optional_relop_t<decltype(std::declval<const T &>() > std::declval<const U &>())>;
|
||||
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_eq_t<T, U> operator==(const optional<T> &lhs, const optional<U> &rhs) { return static_cast<bool>(lhs) == static_cast<bool>(rhs) && (!lhs || *lhs == *rhs); }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_ne_t<T, U> operator!=(const optional<T> &lhs, const optional<U> &rhs) { return static_cast<bool>(lhs) != static_cast<bool>(rhs) || (static_cast<bool>(lhs) && *lhs != *rhs); }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_lt_t<T, U> operator< (const optional<T> &lhs, const optional<U> &rhs) { return static_cast<bool>(rhs) && (!lhs || *lhs < *rhs); }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_gt_t<T, U> operator> (const optional<T> &lhs, const optional<U> &rhs) { return static_cast<bool>(lhs) && (!rhs || *lhs > *rhs); }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_le_t<T, U> operator<=(const optional<T> &lhs, const optional<U> &rhs) { return !lhs || (static_cast<bool>(rhs) && *lhs <= *rhs); }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_ge_t<T, U> operator>=(const optional<T> &lhs, const optional<U> &rhs) { return !rhs || (static_cast<bool>(lhs) && *lhs >= *rhs); }
|
||||
|
||||
template<typename T, std::three_way_comparable_with<T> U>
|
||||
constexpr inline std::compare_three_way_result_t<T, U> operator <=>(const optional<T> &lhs, const optional<U> &rhs) {
|
||||
return (lhs && rhs) ? *lhs <=> *rhs : static_cast<bool>(lhs) <=> static_cast<bool>(rhs);
|
||||
}
|
||||
|
||||
template<typename T> constexpr inline bool operator==(const optional<T> &lhs, nullopt_t) { return !lhs; }
|
||||
|
||||
template<typename T> constexpr inline std::strong_ordering operator<=>(const optional<T> &lhs, nullopt_t) { return static_cast<bool>(lhs) <=> false; }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_eq_t<T, U> operator==(const optional<T> &lhs, const U &rhs) { return lhs && *lhs == rhs; }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_eq_t<U, T> operator==(const U &lhs, const optional<T> &rhs) { return rhs && lhs == *rhs; }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_ne_t<T, U> operator!=(const optional<T> &lhs, const U &rhs) { return !lhs || *lhs != rhs; }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_ne_t<U, T> operator!=(const U &lhs, const optional<T> &rhs) { return !rhs || lhs != *rhs; }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_lt_t<T, U> operator< (const optional<T> &lhs, const U &rhs) { return !lhs || *lhs < rhs; }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_lt_t<U, T> operator< (const U &lhs, const optional<T> &rhs) { return rhs && lhs < *rhs; }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_gt_t<T, U> operator> (const optional<T> &lhs, const U &rhs) { return lhs && *lhs > rhs; }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_gt_t<U, T> operator> (const U &lhs, const optional<T> &rhs) { return !rhs || lhs > *rhs; }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_le_t<T, U> operator<=(const optional<T> &lhs, const U &rhs) { return !lhs || *lhs <= rhs; }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_le_t<U, T> operator<=(const U &lhs, const optional<T> &rhs) { return rhs && lhs <= *rhs; }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_ge_t<T, U> operator>=(const optional<T> &lhs, const U &rhs) { return lhs && *lhs >= rhs; }
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr inline impl::optional_ge_t<U, T> operator>=(const U &lhs, const optional<T> &rhs) { return !rhs || lhs >= *rhs; }
|
||||
|
||||
template<typename T, typename U> requires (!impl::IsOptional<U>) && std::three_way_comparable_with<T, U>
|
||||
constexpr inline std::compare_three_way_result_t<T, U> operator<=>(const optional<T> &lhs, const U &rhs) {
|
||||
return static_cast<bool>(lhs) ? *lhs <=> rhs : std::strong_ordering::less;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr inline std::enable_if_t<std::is_constructible<std::decay_t<T>, T>::value, optional<std::decay_t<T>>> make_optional(T && t) { return optional<std::decay_t<T>>{ std::forward<T>(t) }; }
|
||||
|
||||
template<typename T, typename... Args>
|
||||
constexpr inline std::enable_if_t<std::is_constructible<T, Args...>::value, optional<T>> make_optional(Args &&... args) { return optional<T>{ ::ams::util::in_place, std::forward<Args>(args)... }; }
|
||||
|
||||
template<typename T, typename U, typename... Args>
|
||||
constexpr inline std::enable_if_t<std::is_constructible<T, std::initializer_list<U> &, Args...>::value, optional<T>> make_optional(std::initializer_list<U> il, Args &&... args) { return optional<T>{ ::ams::util::in_place, il, std::forward<Args>(args)... }; }
|
||||
|
||||
template<typename T> optional(T) -> optional<T>;
|
||||
|
||||
}
|
||||
|
||||
namespace std {
|
||||
|
||||
template<typename T>
|
||||
constexpr inline enable_if_t<is_move_constructible_v<T> && is_swappable_v<T>> swap(::ams::util::optional<T> &lhs, ::ams::util::optional<T> &rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr inline enable_if_t<!(is_move_constructible_v<T> && is_swappable_v<T>)> swap(::ams::util::optional<T> &lhs, ::ams::util::optional<T> &rhs) = delete;
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
constexpr inline bool HasOverlap(uintptr_t addr0, size_t size0, uintptr_t addr1, size_t size1) {
|
||||
if (addr0 <= addr1) {
|
||||
return addr1 < addr0 + size0;
|
||||
} else {
|
||||
return addr0 < addr1 + size1;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline bool Contains(uintptr_t addr, size_t size, uintptr_t ptr) {
|
||||
return (addr <= ptr) && (ptr < addr + size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_typed_storage.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace impl {
|
||||
|
||||
#define AMS_UTIL_OFFSET_OF_STANDARD_COMPLIANT 1
|
||||
|
||||
#if AMS_UTIL_OFFSET_OF_STANDARD_COMPLIANT
|
||||
|
||||
template<std::ptrdiff_t Offset, typename P, typename M, auto Ptr>
|
||||
consteval std::strong_ordering TestOffsetForOffsetOfImpl() {
|
||||
#pragma pack(push, 1)
|
||||
const union Union {
|
||||
char c;
|
||||
struct {
|
||||
char padding[Offset];
|
||||
M members[1 + (sizeof(P) / std::max<size_t>(sizeof(M), 1))];
|
||||
};
|
||||
P p;
|
||||
|
||||
constexpr Union() : c() { /* ... */ }
|
||||
constexpr ~Union() { /* ... */ }
|
||||
} U;
|
||||
#pragma pack(pop)
|
||||
|
||||
const M *target = std::addressof(U.p.*Ptr);
|
||||
const M *guess = std::addressof(U.members[0]);
|
||||
|
||||
/* NOTE: target == guess is definitely legal, target < guess is probably legal, definitely legal if Offset <= true offsetof. */
|
||||
/* <=> may or may not be legal, but it definitely seems to work. Evaluate again, if it breaks. */
|
||||
return guess <=> target;
|
||||
|
||||
//if (guess == target) {
|
||||
// return std::strong_ordering::equal;
|
||||
//} else if (guess < target) {
|
||||
// return std::strong_ordering::less;
|
||||
//} else {
|
||||
// return std::strong_ordering::greater;
|
||||
//}
|
||||
}
|
||||
|
||||
template<std::ptrdiff_t Low, std::ptrdiff_t High, typename P, typename M, auto Ptr>
|
||||
consteval std::ptrdiff_t OffsetOfImpl() {
|
||||
static_assert(Low <= High);
|
||||
|
||||
constexpr std::ptrdiff_t Guess = (Low + High) / 2;
|
||||
constexpr auto Order = TestOffsetForOffsetOfImpl<Guess, P, M, Ptr>();
|
||||
|
||||
if constexpr (Order == std::strong_ordering::equal) {
|
||||
return Guess;
|
||||
} else if constexpr (Order == std::strong_ordering::less) {
|
||||
return OffsetOfImpl<Guess + 1, High, P, M, Ptr>();
|
||||
} else {
|
||||
static_assert(Order == std::strong_ordering::greater);
|
||||
return OffsetOfImpl<Low, Guess - 1, P, M, Ptr>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename P, typename M, auto Ptr>
|
||||
struct OffsetOfCalculator {
|
||||
static constexpr const std::ptrdiff_t Value = OffsetOfImpl<0, sizeof(P), P, M, Ptr>();
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template<typename ParentType, typename MemberType, auto Ptr>
|
||||
struct OffsetOfCalculator {
|
||||
private:
|
||||
static consteval std::ptrdiff_t Calculate() {
|
||||
const union Union {
|
||||
ParentType p;
|
||||
char c;
|
||||
|
||||
constexpr Union() : c() { /* ... */ }
|
||||
constexpr ~Union() { /* ... */ }
|
||||
} U;
|
||||
|
||||
const auto *parent = std::addressof(U.p);
|
||||
const auto *target = std::addressof(parent->*Ptr);
|
||||
|
||||
return static_cast<const uint8_t *>(static_cast<const void *>(target)) - static_cast<const uint8_t *>(static_cast<const void *>(parent));
|
||||
}
|
||||
public:
|
||||
static constexpr const std::ptrdiff_t Value = Calculate();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
struct GetMemberPointerTraits;
|
||||
|
||||
template<typename P, typename M>
|
||||
struct GetMemberPointerTraits<M P::*> {
|
||||
using Parent = P;
|
||||
using Member = M;
|
||||
};
|
||||
|
||||
template<auto MemberPtr>
|
||||
using GetParentType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Parent;
|
||||
|
||||
template<auto MemberPtr>
|
||||
using GetMemberType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Member;
|
||||
|
||||
template<auto MemberPtr, typename RealParentType = GetParentType<MemberPtr>> requires (std::derived_from<RealParentType, GetParentType<MemberPtr>> || std::same_as<RealParentType, GetParentType<MemberPtr>>)
|
||||
struct OffsetOf : public std::integral_constant<std::ptrdiff_t, OffsetOfCalculator<RealParentType, GetMemberType<MemberPtr>, MemberPtr>::Value> {};
|
||||
|
||||
#if defined(ATMOSPHERE_COMPILER_CLANG)
|
||||
template<typename ParentType, typename MemberType, auto Ptr>
|
||||
struct OffsetOfCalculatorTheSadWayForClangSupport {
|
||||
static ALWAYS_INLINE std::ptrdiff_t Calculate() {
|
||||
const union Union {
|
||||
ParentType p;
|
||||
char c;
|
||||
|
||||
Union() : c() { /* ... */ }
|
||||
~Union() { /* ... */ }
|
||||
} U;
|
||||
|
||||
const auto *parent = std::addressof(U.p);
|
||||
const auto *target = std::addressof(parent->*Ptr);
|
||||
|
||||
return static_cast<const uint8_t *>(static_cast<const void *>(target)) - static_cast<const uint8_t *>(static_cast<const void *>(parent));
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
ALWAYS_INLINE RealParentType &GetParentReference(impl::GetMemberType<MemberPtr> *member) {
|
||||
/* TODO: If clang resolves compiler-bugs in consteval (or if my std proposal makes it into a future C++ standard), we should go back to constexpr. */
|
||||
#if defined(ATMOSPHERE_COMPILER_CLANG)
|
||||
const std::ptrdiff_t Offset = impl::OffsetOfCalculatorTheSadWayForClangSupport<RealParentType, impl::GetMemberType<MemberPtr>, MemberPtr>::Calculate();
|
||||
#else
|
||||
constexpr std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>::value;
|
||||
#endif
|
||||
return *static_cast<RealParentType *>(static_cast<void *>(static_cast<uint8_t *>(static_cast<void *>(member)) - Offset));
|
||||
}
|
||||
|
||||
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
ALWAYS_INLINE RealParentType const &GetParentReference(impl::GetMemberType<MemberPtr> const *member) {
|
||||
/* TODO: If clang resolves compiler-bugs in consteval (or if my std proposal makes it into a future C++ standard), we should go back to constexpr. */
|
||||
#if defined(ATMOSPHERE_COMPILER_CLANG)
|
||||
const std::ptrdiff_t Offset = impl::OffsetOfCalculatorTheSadWayForClangSupport<RealParentType, impl::GetMemberType<MemberPtr>, MemberPtr>::Calculate();
|
||||
#else
|
||||
constexpr std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>::value;
|
||||
#endif
|
||||
return *static_cast<const RealParentType *>(static_cast<const void *>(static_cast<const uint8_t *>(static_cast<const void *>(member)) - Offset));
|
||||
}
|
||||
|
||||
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
ALWAYS_INLINE RealParentType *GetParentPointer(impl::GetMemberType<MemberPtr> *member) {
|
||||
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
||||
}
|
||||
|
||||
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
ALWAYS_INLINE RealParentType const *GetParentPointer(impl::GetMemberType<MemberPtr> const *member) {
|
||||
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
||||
}
|
||||
|
||||
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
ALWAYS_INLINE RealParentType &GetParentReference(impl::GetMemberType<MemberPtr> &member) {
|
||||
return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
|
||||
}
|
||||
|
||||
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
ALWAYS_INLINE RealParentType const &GetParentReference(impl::GetMemberType<MemberPtr> const &member) {
|
||||
return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
|
||||
}
|
||||
|
||||
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
ALWAYS_INLINE RealParentType *GetParentPointer(impl::GetMemberType<MemberPtr> &member) {
|
||||
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
||||
}
|
||||
|
||||
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
ALWAYS_INLINE RealParentType const *GetParentPointer(impl::GetMemberType<MemberPtr> const &member) {
|
||||
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
||||
}
|
||||
|
||||
|
||||
/* Defines, for use by other code. */
|
||||
#define AMS_OFFSETOF(parent, member) (__builtin_offsetof(parent, member))
|
||||
#define AMS_GET_PARENT_PTR(parent, member, _arg) (::ams::util::GetParentPointer<&parent::member, parent>(_arg))
|
||||
#define AMS_GET_PARENT_REF(parent, member, _arg) (::ams::util::GetParentReference<&parent::member, parent>(_arg))
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
namespace ams::util {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T>
|
||||
struct IsSharedPointerImpl : public std::false_type{};
|
||||
|
||||
template<typename T>
|
||||
struct IsSharedPointerImpl<std::shared_ptr<T>> : public std::true_type{};
|
||||
|
||||
template<typename T>
|
||||
struct IsUniquePointerImpl : public std::false_type{};
|
||||
|
||||
template<typename T>
|
||||
struct IsUniquePointerImpl<std::unique_ptr<T>> : public std::true_type{};
|
||||
|
||||
template<typename T, typename U>
|
||||
concept PointerToImpl = std::same_as<typename std::pointer_traits<T>::element_type, U>;
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
concept IsRawPointer = std::is_pointer<T>::value;
|
||||
|
||||
template<typename T>
|
||||
concept IsSharedPointer = impl::IsSharedPointerImpl<T>::value;
|
||||
|
||||
template<typename T>
|
||||
concept IsUniquePointer = impl::IsUniquePointerImpl<T>::value;
|
||||
|
||||
template<typename T>
|
||||
concept IsSmartPointer = IsSharedPointer<T> || IsUniquePointer<T>;
|
||||
|
||||
template<typename T>
|
||||
concept IsRawOrSmartPointer = IsRawPointer<T> || IsSmartPointer<T>;
|
||||
|
||||
template<typename T, typename U>
|
||||
concept SmartPointerTo = IsSmartPointer<T> && impl::PointerToImpl<T, U>;
|
||||
|
||||
template<typename T, typename U>
|
||||
concept RawOrSmartPointerTo = IsRawOrSmartPointer<T> && impl::PointerToImpl<T, U>;
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util::range {
|
||||
|
||||
template<typename T, typename F>
|
||||
constexpr bool any_of(T &&t, F &&f) {
|
||||
return std::any_of(std::begin(t), std::end(t), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename T, typename F>
|
||||
constexpr bool all_of(T &&t, F &&f) {
|
||||
return std::all_of(std::begin(t), std::end(t), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename T, typename F>
|
||||
constexpr bool none_of(T &&t, F &&f) {
|
||||
return std::none_of(std::begin(t), std::end(t), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename T, typename F>
|
||||
constexpr auto find_if(T &&t, F &&f) {
|
||||
return std::find_if(std::begin(t), std::end(t), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename T, typename F>
|
||||
constexpr auto for_each(T &&t, F &&f) {
|
||||
return std::for_each(std::begin(t), std::end(t), std::forward<F>(f));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<class F>
|
||||
class ScopeGuard {
|
||||
NON_COPYABLE(ScopeGuard);
|
||||
private:
|
||||
F f;
|
||||
bool active;
|
||||
public:
|
||||
constexpr ALWAYS_INLINE ScopeGuard(F f) : f(std::move(f)), active(true) { }
|
||||
constexpr ALWAYS_INLINE ~ScopeGuard() { if (active) { f(); } }
|
||||
constexpr ALWAYS_INLINE void Cancel() { active = false; }
|
||||
|
||||
constexpr ALWAYS_INLINE ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
|
||||
rhs.Cancel();
|
||||
}
|
||||
|
||||
ScopeGuard &operator=(ScopeGuard&& rhs) = delete;
|
||||
};
|
||||
|
||||
template<class F>
|
||||
constexpr ALWAYS_INLINE ScopeGuard<F> MakeScopeGuard(F f) {
|
||||
return ScopeGuard<F>(std::move(f));
|
||||
}
|
||||
|
||||
enum class ScopeGuardOnExit {};
|
||||
|
||||
template <typename F>
|
||||
constexpr ALWAYS_INLINE ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
|
||||
return ScopeGuard<F>(std::forward<F>(f));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define SCOPE_GUARD ::ams::util::impl::ScopeGuardOnExit() + [&]() ALWAYS_INLINE_LAMBDA
|
||||
#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
/* std::size() does not support zero-size C arrays. We're fixing that. */
|
||||
template<class C>
|
||||
constexpr auto size(const C& c) -> decltype(c.size()) {
|
||||
return std::size(c);
|
||||
}
|
||||
|
||||
template<class C>
|
||||
constexpr std::size_t size(const C& c) {
|
||||
if constexpr (sizeof(C) == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return std::size(c);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<class T, template <class...> class Template>
|
||||
struct is_specialization_of : std::false_type{};
|
||||
|
||||
template<template <class...> class Template, class... Args>
|
||||
struct is_specialization_of<Template<Args...>, Template> : std::true_type{};
|
||||
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename T>
|
||||
constexpr T ToLower(T c) {
|
||||
return ('A' <= c && c <= 'Z') ? (c - 'A' + 'a') : c;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T ToUpper(T c) {
|
||||
return ('a' <= c && c <= 'z') ? (c - 'a' + 'A') : c;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int Strncmp(const T *lhs, const T *rhs, int count) {
|
||||
AMS_ASSERT(lhs != nullptr);
|
||||
AMS_ASSERT(rhs != nullptr);
|
||||
AMS_ABORT_UNLESS(count >= 0);
|
||||
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
T l, r;
|
||||
do {
|
||||
l = *(lhs++);
|
||||
r = *(rhs++);
|
||||
} while (l && (l == r) && (--count));
|
||||
|
||||
return l - r;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int Strnicmp(const T *lhs, const T *rhs, int count) {
|
||||
AMS_ASSERT(lhs != nullptr);
|
||||
AMS_ASSERT(rhs != nullptr);
|
||||
AMS_ABORT_UNLESS(count >= 0);
|
||||
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
T l, r;
|
||||
do {
|
||||
l = ::ams::util::ToLower(*(lhs++));
|
||||
r = ::ams::util::ToLower(*(rhs++));
|
||||
} while (l && (l == r) && (--count));
|
||||
|
||||
return l - r;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int Strlcpy(T *dst, const T *src, int count) {
|
||||
AMS_ASSERT(dst != nullptr);
|
||||
AMS_ASSERT(src != nullptr);
|
||||
|
||||
const T *cur = src;
|
||||
if (count > 0) {
|
||||
while ((--count) && *cur) {
|
||||
*(dst++) = *(cur++);
|
||||
}
|
||||
*dst = 0;
|
||||
}
|
||||
|
||||
while (*cur) {
|
||||
cur++;
|
||||
}
|
||||
|
||||
return static_cast<int>(cur - src);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int Strlen(const T *str) {
|
||||
AMS_ASSERT(str != nullptr);
|
||||
|
||||
int length = 0;
|
||||
while (*str++) {
|
||||
++length;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int Strnlen(const T *str, int count) {
|
||||
AMS_ASSERT(str != nullptr);
|
||||
AMS_ASSERT(count >= 0);
|
||||
|
||||
int length = 0;
|
||||
while (count-- && *str++) {
|
||||
++length;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
ALWAYS_INLINE void *Memchr(void *s, int c, size_t n) {
|
||||
return const_cast<void*>(::memchr(s, c, n));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE const void *Memchr(const void *s, int c, size_t n) {
|
||||
return ::memchr(s, c, n);
|
||||
}
|
||||
|
||||
inline void *Memrchr(void *s, int c, size_t n) {
|
||||
#if !(defined(__MINGW32__) || defined(__MINGW64__) || defined(ATMOSPHERE_OS_MACOS))
|
||||
return const_cast<void *>(::memrchr(s, c, n));
|
||||
#else
|
||||
/* TODO: Optimized implementation? */
|
||||
if (AMS_LIKELY(n > 0)) {
|
||||
const u8 *p = static_cast<const u8 *>(s);
|
||||
const u8 v = static_cast<u8>(c);
|
||||
while ((n--) != 0) {
|
||||
if (p[n] == v) {
|
||||
return const_cast<void *>(static_cast<const void *>(p + n));
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline const void *Memrchr(const void *s, int c, size_t n) {
|
||||
#if !(defined(__MINGW32__) || defined(__MINGW64__) || defined(ATMOSPHERE_OS_MACOS))
|
||||
return ::memrchr(s, c, n);
|
||||
#else
|
||||
/* TODO: Optimized implementation? */
|
||||
if (AMS_LIKELY(n > 0)) {
|
||||
const u8 *p = static_cast<const u8 *>(s);
|
||||
const u8 v = static_cast<u8>(c);
|
||||
while ((n--) != 0) {
|
||||
if (p[n] == v) {
|
||||
return static_cast<const void *>(p + n);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,347 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<class _CharT, class _Traits = std::char_traits<_CharT>>
|
||||
class basic_string_view {
|
||||
static_assert(!std::is_array<_CharT>::value);
|
||||
static_assert(std::is_trivial<_CharT>::value && std::is_standard_layout<_CharT>::value);
|
||||
static_assert(std::same_as<_CharT, typename _Traits::char_type>);
|
||||
public:
|
||||
using traits_type = _Traits;
|
||||
using value_type = _CharT;
|
||||
using pointer = value_type *;
|
||||
using const_pointer = const value_type *;
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using const_iterator = const value_type *;
|
||||
using iterator = const_iterator;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using reverse_iterator = const_reverse_iterator;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
static constexpr size_type npos = size_type(-1);
|
||||
private:
|
||||
static constexpr int _s_compare(size_type lhs, size_type rhs) noexcept {
|
||||
const difference_type diff = lhs - rhs;
|
||||
if (diff > std::numeric_limits<int>::max()) {
|
||||
return std::numeric_limits<int>::max();
|
||||
}
|
||||
if (diff < std::numeric_limits<int>::min()) {
|
||||
return std::numeric_limits<int>::min();
|
||||
}
|
||||
return static_cast<int>(diff);
|
||||
}
|
||||
private:
|
||||
const_pointer m_str;
|
||||
size_type m_len;
|
||||
public:
|
||||
constexpr basic_string_view() noexcept : m_str(nullptr), m_len(0) { /* ... */ }
|
||||
constexpr basic_string_view(const _CharT *str, size_type len) noexcept : m_str(str), m_len(len) { /* ... */ }
|
||||
constexpr basic_string_view(const _CharT *str) noexcept : m_str(str), m_len(str ? traits_type::length(str) : 0) { /* ... */ }
|
||||
|
||||
template<std::contiguous_iterator _It, std::sized_sentinel_for<_It> _End> requires std::same_as<std::iter_value_t<_It>, _CharT> && (!std::convertible_to<_End, size_type>)
|
||||
constexpr basic_string_view(_It first, _End last) noexcept : m_str(std::to_address(first)), m_len(last - first) { /* ... */ }
|
||||
|
||||
constexpr basic_string_view(const basic_string_view &) noexcept = default;
|
||||
constexpr basic_string_view &operator=(const basic_string_view &) noexcept = default;
|
||||
|
||||
constexpr const_iterator begin() const noexcept { return m_str; }
|
||||
constexpr const_iterator end() const noexcept { return m_str + m_len; }
|
||||
|
||||
constexpr const_iterator cbegin() const noexcept { return m_str; }
|
||||
constexpr const_iterator cend() const noexcept { return m_str + m_len; }
|
||||
|
||||
constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(this->end()); }
|
||||
constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(this->begin()); }
|
||||
|
||||
constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(this->cend()); }
|
||||
constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(this->cbegin()); }
|
||||
|
||||
constexpr size_type size() const noexcept { return m_len; }
|
||||
constexpr size_type length() const noexcept { return m_len; }
|
||||
|
||||
constexpr size_type max_size() const noexcept { return (npos - sizeof(size_type) - sizeof(void *)) / sizeof(value_type) / 4; }
|
||||
|
||||
[[nodiscard]] constexpr bool empty() const noexcept { return m_len == 0; }
|
||||
|
||||
constexpr const_reference operator[](size_type pos) const noexcept {
|
||||
AMS_ASSERT(pos < m_len);
|
||||
return *(m_str + pos);
|
||||
}
|
||||
|
||||
constexpr const_reference at(size_type pos) const noexcept {
|
||||
AMS_ASSERT(pos < m_len);
|
||||
AMS_ABORT_UNLESS(pos < m_len);
|
||||
return *(m_str + pos);
|
||||
}
|
||||
|
||||
constexpr const_reference front() const noexcept {
|
||||
AMS_ASSERT(m_len > 0);
|
||||
return *m_str;
|
||||
}
|
||||
|
||||
constexpr const_reference back() const noexcept {
|
||||
AMS_ASSERT(m_len > 0);
|
||||
return *(m_str + m_len - 1);
|
||||
}
|
||||
|
||||
constexpr const_pointer data() const noexcept { return m_str; }
|
||||
|
||||
constexpr void remove_prefix(size_type n) noexcept {
|
||||
AMS_ASSERT(m_len >= n);
|
||||
m_str += n;
|
||||
m_len -= n;
|
||||
}
|
||||
|
||||
constexpr void remove_suffix(size_type n) noexcept {
|
||||
AMS_ASSERT(m_len >= n);
|
||||
m_len -= n;
|
||||
}
|
||||
|
||||
constexpr void swap(basic_string_view &rhs) noexcept {
|
||||
auto tmp = *this;
|
||||
*this = rhs;
|
||||
rhs = tmp;
|
||||
}
|
||||
|
||||
constexpr size_type copy(_CharT *str, size_type n, size_type pos = 0) const noexcept {
|
||||
AMS_ASSERT(pos <= this->size());
|
||||
const size_type rlen = std::min(n, m_len - pos);
|
||||
traits_type::copy(str, this->data() + pos, rlen);
|
||||
return rlen;
|
||||
}
|
||||
|
||||
constexpr basic_string_view substr(size_type pos = 0, size_type n = npos) const noexcept {
|
||||
AMS_ASSERT(pos <= this->size());
|
||||
const size_type rlen = std::min(n, m_len - pos);
|
||||
return basic_string_view{m_str + pos, rlen};
|
||||
}
|
||||
|
||||
constexpr int compare(basic_string_view str) const noexcept {
|
||||
const size_type rlen = std::min(m_len, str.m_len);
|
||||
int ret = traits_type::compare(m_str, str.m_str, rlen);
|
||||
if (ret == 0) {
|
||||
ret = _s_compare(m_len, str.m_len);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
constexpr int compare(size_type pos, size_type n, basic_string_view str) const noexcept {
|
||||
return this->substr(pos, n).compare(str);
|
||||
}
|
||||
|
||||
constexpr int compare(size_type pos1, size_type n1, basic_string_view str, size_type pos2, size_type n2) const {
|
||||
return this->substr(pos1, n1).compare(str.substr(pos2, n2));
|
||||
}
|
||||
|
||||
constexpr int compare(const _CharT *str) const noexcept {
|
||||
return this->compare(basic_string_view(str));
|
||||
}
|
||||
|
||||
constexpr int compare(size_type pos, size_type n, const _CharT *str) const noexcept {
|
||||
return this->substr(pos, n).compare(basic_string_view(str));
|
||||
}
|
||||
|
||||
constexpr int compare(size_type pos, size_type n, const _CharT *str, size_type n2) const noexcept {
|
||||
return this->substr(pos, n).compare(basic_string_view(str, n2));
|
||||
}
|
||||
|
||||
constexpr bool starts_with(basic_string_view x) const noexcept { return this->substr(0, x.size()) == x; }
|
||||
constexpr bool starts_with(_CharT x) const noexcept { return !this->empty() && traits_type::eq(this->front(), x); }
|
||||
constexpr bool starts_with(const _CharT *x) const noexcept { return this->starts_with(basic_string_view(x)); }
|
||||
|
||||
constexpr bool ends_with(basic_string_view x) const noexcept { return this->size() >= x.size() && this->compare(this->size() - x.size(), npos, x) == 0; }
|
||||
constexpr bool ends_with(_CharT x) const noexcept { return !this->empty() && traits_type::eq(this->back(), x); }
|
||||
constexpr bool ends_with(const _CharT *x) const noexcept { return this->ends_with(basic_string_view(x)); }
|
||||
|
||||
constexpr size_type find(const _CharT *str, size_type pos, size_type n) const noexcept {
|
||||
if (n == 0) {
|
||||
return pos + m_len ? pos : npos;
|
||||
}
|
||||
|
||||
if (n <= m_len) {
|
||||
for (/* ... */; pos <= m_len - n; ++pos) {
|
||||
if (traits_type::eq(m_str[pos], str[0]) && traits_type::compare(m_str + pos + 1, str + 1, n - 1) == 0) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type find(_CharT c, size_type pos = 0) const noexcept {
|
||||
size_type ret = npos;
|
||||
if (pos < m_len) {
|
||||
const size_type n = m_len - pos;
|
||||
if (const _CharT *p = traits_type::find(m_str + pos, n, c); p) {
|
||||
ret = p - m_str;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
constexpr size_type find(basic_string_view str, size_type pos = 0) const noexcept { return this->find(str.m_str, pos, str.m_len); }
|
||||
|
||||
__attribute__((nonnull(2)))
|
||||
constexpr size_type find(const _CharT *str, size_type pos = 0) const noexcept { return this->find(str, pos, traits_type::length(str)); }
|
||||
|
||||
constexpr size_type rfind(const _CharT *str, size_type pos, size_type n) const noexcept {
|
||||
if (n <= m_len) {
|
||||
pos = std::min(size_type(m_len - n), pos);
|
||||
do {
|
||||
if (traits_type::compare(m_str + pos, str, n) == 0) {
|
||||
return pos;
|
||||
}
|
||||
} while (pos-- > 0);
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type rfind(_CharT c, size_type pos = 0) const noexcept {
|
||||
size_type size = m_len;
|
||||
if (size > 0) {
|
||||
if (--size > pos) {
|
||||
size = pos;
|
||||
}
|
||||
for (++size; size-- > 0; /* ... */) {
|
||||
if (traits_type::eq(m_str[size], c)) {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type rfind(basic_string_view str, size_type pos = 0) const noexcept { return this->rfind(str.m_str, pos, str.m_len); }
|
||||
|
||||
__attribute__((nonnull(2)))
|
||||
constexpr size_type rfind(const _CharT *str, size_type pos = 0) const noexcept { return this->rfind(str, pos, traits_type::length(str)); }
|
||||
|
||||
constexpr size_type find_first_of(const _CharT *str, size_type pos, size_t n) const noexcept {
|
||||
for (/* ... */; n && pos < m_len; ++pos) {
|
||||
if (const _CharT *p = traits_type::find(str, n, m_str[pos]); p) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type find_first_of(basic_string_view str, size_type pos = 0) const noexcept { return this->find_first_of(str.m_str, pos, str.m_len); }
|
||||
constexpr size_type find_first_of(_CharT c, size_type pos = 0) const noexcept { return this->find(c, pos); }
|
||||
|
||||
__attribute__((nonnull(2)))
|
||||
constexpr size_type find_first_of(const _CharT *str, size_type pos = 0) const noexcept { return this->find_first_of(str, pos, traits_type::length(str)); }
|
||||
|
||||
constexpr size_type find_last_of(const _CharT *str, size_type pos, size_t n) const noexcept {
|
||||
size_type size = this->size();
|
||||
if (size && n) {
|
||||
if (--size > pos) {
|
||||
size = pos;
|
||||
}
|
||||
do {
|
||||
if (traits_type::find(str, n, m_str[size])) {
|
||||
return size;
|
||||
}
|
||||
} while (size-- != 0);
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type find_last_of(basic_string_view str, size_type pos = 0) const noexcept { return this->find_last_of(str.m_str, pos, str.m_len); }
|
||||
constexpr size_type find_last_of(_CharT c, size_type pos = 0) const noexcept { return this->rfind(c, pos); }
|
||||
|
||||
__attribute__((nonnull(2)))
|
||||
constexpr size_type find_last_of(const _CharT *str, size_type pos = 0) const noexcept { return this->find_first_of(str, pos, traits_type::length(str)); }
|
||||
|
||||
constexpr size_type find_first_not_of(const _CharT *str, size_type pos, size_t n) const noexcept {
|
||||
for (/* ... */; pos < m_len; ++pos) {
|
||||
if (!traits_type::find(str, n, m_str[pos])) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type find_first_not_of(_CharT c, size_type pos = 0) const noexcept {
|
||||
for (/* ... */; pos < m_len; ++pos) {
|
||||
if (!traits_type::eq(m_str[pos], c)) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type find_first_not_of(basic_string_view str, size_type pos = 0) const noexcept { return this->find_first_not_of(str.m_str, pos, str.m_len); }
|
||||
|
||||
__attribute__((nonnull(2)))
|
||||
constexpr size_type find_first_not_of(const _CharT *str, size_type pos = 0) const noexcept { return this->find_first_not_of(str, pos, traits_type::length(str)); }
|
||||
|
||||
constexpr size_type find_last_not_of(const _CharT *str, size_type pos, size_t n) const noexcept {
|
||||
size_type size = this->size();
|
||||
if (size) {
|
||||
if (--size > pos) {
|
||||
size = pos;
|
||||
}
|
||||
do {
|
||||
if (!traits_type::find(str, n, m_str[size])) {
|
||||
return size;
|
||||
}
|
||||
} while (size-- != 0);
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type find_last_not_of(_CharT c, size_type pos = 0) const noexcept {
|
||||
size_type size = this->size();
|
||||
if (size) {
|
||||
if (--size > pos) {
|
||||
size = pos;
|
||||
}
|
||||
do {
|
||||
if (!traits_type::eq(m_str[size], c)) {
|
||||
return size;
|
||||
}
|
||||
} while (size-- != 0);
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type find_last_not_of(basic_string_view str, size_type pos = 0) const noexcept { return this->find_last_not_of(str.m_str, pos, str.m_len); }
|
||||
|
||||
__attribute__((nonnull(2)))
|
||||
constexpr size_type find_last_not_of(const _CharT *str, size_type pos = 0) const noexcept { return this->find_last_not_of(str, pos, traits_type::length(str)); }
|
||||
|
||||
constexpr friend bool operator==(const basic_string_view &lhs, const basic_string_view &rhs) noexcept { return lhs.compare(rhs) == 0; }
|
||||
constexpr friend bool operator!=(const basic_string_view &lhs, const basic_string_view &rhs) noexcept { return lhs.compare(rhs) != 0; }
|
||||
constexpr friend bool operator<=(const basic_string_view &lhs, const basic_string_view &rhs) noexcept { return lhs.compare(rhs) <= 0; }
|
||||
constexpr friend bool operator>=(const basic_string_view &lhs, const basic_string_view &rhs) noexcept { return lhs.compare(rhs) >= 0; }
|
||||
constexpr friend bool operator< (const basic_string_view &lhs, const basic_string_view &rhs) noexcept { return lhs.compare(rhs) < 0; }
|
||||
constexpr friend bool operator> (const basic_string_view &lhs, const basic_string_view &rhs) noexcept { return lhs.compare(rhs) > 0; }
|
||||
};
|
||||
|
||||
template<std::contiguous_iterator _It, std::sized_sentinel_for<_It> _End>
|
||||
basic_string_view(_It, _End) -> basic_string_view<std::iter_value_t<_It>>;
|
||||
|
||||
using string_view = basic_string_view<char>;
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
u32 GetMicroSeconds();
|
||||
void WaitMicroSeconds(int us);
|
||||
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
/* Implementation of TinyMT (mersenne twister RNG). */
|
||||
/* Like Nintendo, we will use the sample parameters. */
|
||||
class TinyMT {
|
||||
public:
|
||||
static constexpr size_t NumStateWords = 4;
|
||||
|
||||
struct State {
|
||||
u32 data[NumStateWords];
|
||||
};
|
||||
private:
|
||||
static constexpr u32 ParamMat1 = 0x8F7011EE;
|
||||
static constexpr u32 ParamMat2 = 0xFC78FF1F;
|
||||
static constexpr u32 ParamTmat = 0x3793FDFF;
|
||||
|
||||
static constexpr u32 ParamMult = 0x6C078965;
|
||||
static constexpr u32 ParamPlus = 0x0019660D;
|
||||
static constexpr u32 ParamXor = 0x5D588B65;
|
||||
|
||||
static constexpr u32 TopBitmask = 0x7FFFFFFF;
|
||||
|
||||
static constexpr int MinimumInitIterations = 8;
|
||||
static constexpr int NumDiscardedInitOutputs = 8;
|
||||
|
||||
static constexpr inline u32 XorByShifted27(u32 value) {
|
||||
return value ^ (value >> 27);
|
||||
}
|
||||
|
||||
static constexpr inline u32 XorByShifted30(u32 value) {
|
||||
return value ^ (value >> 30);
|
||||
}
|
||||
private:
|
||||
State m_state;
|
||||
private:
|
||||
/* Internal API. */
|
||||
void FinalizeInitialization() {
|
||||
const u32 state0 = m_state.data[0] & TopBitmask;
|
||||
const u32 state1 = m_state.data[1];
|
||||
const u32 state2 = m_state.data[2];
|
||||
const u32 state3 = m_state.data[3];
|
||||
|
||||
if (state0 == 0 && state1 == 0 && state2 == 0 && state3 == 0) {
|
||||
m_state.data[0] = 'T';
|
||||
m_state.data[1] = 'I';
|
||||
m_state.data[2] = 'N';
|
||||
m_state.data[3] = 'Y';
|
||||
}
|
||||
|
||||
for (int i = 0; i < NumDiscardedInitOutputs; i++) {
|
||||
this->GenerateRandomU32();
|
||||
}
|
||||
}
|
||||
|
||||
u32 GenerateRandomU24() { return (this->GenerateRandomU32() >> 8); }
|
||||
|
||||
static void GenerateInitialValuePlus(TinyMT::State *state, int index, u32 value) {
|
||||
u32 &state0 = state->data[(index + 0) % NumStateWords];
|
||||
u32 &state1 = state->data[(index + 1) % NumStateWords];
|
||||
u32 &state2 = state->data[(index + 2) % NumStateWords];
|
||||
u32 &state3 = state->data[(index + 3) % NumStateWords];
|
||||
|
||||
const u32 x = XorByShifted27(state0 ^ state1 ^ state3) * ParamPlus;
|
||||
const u32 y = x + index + value;
|
||||
|
||||
state0 = y;
|
||||
state1 += x;
|
||||
state2 += y;
|
||||
}
|
||||
|
||||
static void GenerateInitialValueXor(TinyMT::State *state, int index) {
|
||||
u32 &state0 = state->data[(index + 0) % NumStateWords];
|
||||
u32 &state1 = state->data[(index + 1) % NumStateWords];
|
||||
u32 &state2 = state->data[(index + 2) % NumStateWords];
|
||||
u32 &state3 = state->data[(index + 3) % NumStateWords];
|
||||
|
||||
const u32 x = XorByShifted27(state0 + state1 + state3) * ParamXor;
|
||||
const u32 y = x - index;
|
||||
|
||||
state0 = y;
|
||||
state1 ^= x;
|
||||
state2 ^= y;
|
||||
}
|
||||
public:
|
||||
constexpr explicit TinyMT(util::ConstantInitializeTag) : m_state() { /* ... */ }
|
||||
explicit TinyMT() { /* ... */ }
|
||||
|
||||
/* Initialization. */
|
||||
void Initialize(u32 seed) {
|
||||
m_state.data[0] = seed;
|
||||
m_state.data[1] = ParamMat1;
|
||||
m_state.data[2] = ParamMat2;
|
||||
m_state.data[3] = ParamTmat;
|
||||
|
||||
for (int i = 1; i < MinimumInitIterations; i++) {
|
||||
const u32 mixed = XorByShifted30(m_state.data[(i - 1) % NumStateWords]);
|
||||
m_state.data[i % NumStateWords] ^= mixed * ParamMult + i;
|
||||
}
|
||||
|
||||
this->FinalizeInitialization();
|
||||
}
|
||||
|
||||
void Initialize(const u32 *seed, int seed_count) {
|
||||
m_state.data[0] = 0;
|
||||
m_state.data[1] = ParamMat1;
|
||||
m_state.data[2] = ParamMat2;
|
||||
m_state.data[3] = ParamTmat;
|
||||
|
||||
{
|
||||
const int num_init_iterations = std::max(seed_count + 1, MinimumInitIterations) - 1;
|
||||
|
||||
GenerateInitialValuePlus(std::addressof(m_state), 0, seed_count);
|
||||
|
||||
for (int i = 0; i < num_init_iterations; i++) {
|
||||
GenerateInitialValuePlus(std::addressof(m_state), (i + 1) % NumStateWords, (i < seed_count) ? seed[i] : 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < static_cast<int>(NumStateWords); i++) {
|
||||
GenerateInitialValueXor(std::addressof(m_state), (i + 1 + num_init_iterations) % NumStateWords);
|
||||
}
|
||||
}
|
||||
|
||||
this->FinalizeInitialization();
|
||||
}
|
||||
|
||||
/* State management. */
|
||||
void GetState(TinyMT::State *out) const {
|
||||
std::memcpy(out->data, m_state.data, sizeof(m_state));
|
||||
}
|
||||
|
||||
void SetState(const TinyMT::State *state) {
|
||||
std::memcpy(m_state.data, state->data, sizeof(m_state));
|
||||
}
|
||||
|
||||
/* Random generation. */
|
||||
NOINLINE void GenerateRandomBytes(void *dst, size_t size) {
|
||||
const uintptr_t start = reinterpret_cast<uintptr_t>(dst);
|
||||
const uintptr_t end = start + size;
|
||||
const uintptr_t aligned_start = util::AlignUp(start, 4);
|
||||
const uintptr_t aligned_end = util::AlignDown(end, 4);
|
||||
|
||||
/* Make sure we're aligned. */
|
||||
if (start < aligned_start) {
|
||||
const u32 rnd = this->GenerateRandomU32();
|
||||
std::memcpy(dst, std::addressof(rnd), aligned_start - start);
|
||||
}
|
||||
|
||||
/* Write as many aligned u32s as we can. */
|
||||
{
|
||||
u32 * cur_dst = reinterpret_cast<u32 *>(aligned_start);
|
||||
u32 * const end_dst = reinterpret_cast<u32 *>(aligned_end);
|
||||
|
||||
while (cur_dst < end_dst) {
|
||||
*(cur_dst++) = this->GenerateRandomU32();
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle any leftover unaligned data. */
|
||||
if (aligned_end < end) {
|
||||
const u32 rnd = this->GenerateRandomU32();
|
||||
std::memcpy(reinterpret_cast<void *>(aligned_end), std::addressof(rnd), end - aligned_end);
|
||||
}
|
||||
}
|
||||
|
||||
NOINLINE u32 GenerateRandomU32() {
|
||||
/* Advance state. */
|
||||
const u32 x0 = (m_state.data[0] & TopBitmask) ^ m_state.data[1] ^ m_state.data[2];
|
||||
const u32 y0 = m_state.data[3];
|
||||
const u32 x1 = x0 ^ (x0 << 1);
|
||||
const u32 y1 = y0 ^ (y0 >> 1) ^ x1;
|
||||
|
||||
const u32 state0 = m_state.data[1];
|
||||
u32 state1 = m_state.data[2];
|
||||
u32 state2 = x1 ^ (y1 << 10);
|
||||
const u32 state3 = y1;
|
||||
|
||||
if ((y1 & 1) != 0) {
|
||||
state1 ^= ParamMat1;
|
||||
state2 ^= ParamMat2;
|
||||
}
|
||||
|
||||
m_state.data[0] = state0;
|
||||
m_state.data[1] = state1;
|
||||
m_state.data[2] = state2;
|
||||
m_state.data[3] = state3;
|
||||
|
||||
/* Temper. */
|
||||
const u32 t1 = state0 + (state2 >> 8);
|
||||
u32 t0 = state3 ^ t1;
|
||||
|
||||
if ((t1 & 1) != 0) {
|
||||
t0 ^= ParamTmat;
|
||||
}
|
||||
|
||||
return t0;
|
||||
}
|
||||
|
||||
inline u64 GenerateRandomU64() {
|
||||
const u32 lo = this->GenerateRandomU32();
|
||||
const u32 hi = this->GenerateRandomU32();
|
||||
return (static_cast<u64>(hi) << 32) | static_cast<u64>(lo);
|
||||
}
|
||||
|
||||
inline float GenerateRandomF32() {
|
||||
/* Floats have 24 bits of mantissa. */
|
||||
constexpr int MantissaBits = 24;
|
||||
return GenerateRandomU24() * (1.0f / (1ul << MantissaBits));
|
||||
}
|
||||
|
||||
inline double GenerateRandomF64() {
|
||||
/* Doubles have 53 bits of mantissa. */
|
||||
/* The smart way to generate 53 bits of random would be to use 32 bits */
|
||||
/* from the first rnd32() call, and then 21 from the second. */
|
||||
/* Nintendo does not. They use (32 - 5) = 27 bits from the first rnd32() */
|
||||
/* call, and (32 - 6) bits from the second. We'll do what they do, but */
|
||||
/* There's not a clear reason why. */
|
||||
constexpr int MantissaBits = 53;
|
||||
constexpr int Shift1st = (64 - MantissaBits) / 2;
|
||||
constexpr int Shift2nd = (64 - MantissaBits) - Shift1st;
|
||||
|
||||
const u32 first = (this->GenerateRandomU32() >> Shift1st);
|
||||
const u32 second = (this->GenerateRandomU32() >> Shift2nd);
|
||||
|
||||
return (1.0 * first * (static_cast<u64>(1) << (32 - Shift2nd)) + second) * (1.0 / (static_cast<u64>(1) << MantissaBits));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename T>
|
||||
using is_pod = std::bool_constant<std::is_standard_layout<T>::value && std::is_trivial<T>::value>;
|
||||
|
||||
struct ConstantInitializeTag final {};
|
||||
constexpr inline const ConstantInitializeTag ConstantInitialize{};
|
||||
|
||||
namespace impl {
|
||||
|
||||
constexpr int ToIntegerForIsConstexprConstructible(...) { return {}; }
|
||||
|
||||
template<typename T, auto...Lambdas> requires (std::is_constructible<T, decltype(Lambdas())...>::value)
|
||||
using ToIntegralConstantForIsConstexprConstructible = std::integral_constant<int, ToIntegerForIsConstexprConstructible(T(Lambdas()...))>;
|
||||
|
||||
template<typename T, auto...Lambdas, int = ToIntegralConstantForIsConstexprConstructible<T, Lambdas...>::value>
|
||||
std::true_type IsConstexprConstructibleImpl(int);
|
||||
|
||||
template<typename T, auto...Lambdas>
|
||||
std::false_type IsConstexprConstructibleImpl(long);
|
||||
|
||||
template<typename T>
|
||||
consteval inline auto ConvertToLambdaForIsConstexprConstructible() { return [] { return T{}; }; }
|
||||
|
||||
template<auto V>
|
||||
consteval inline auto ConvertToLambdaForIsConstexprConstructible() { return [] { return V; }; }
|
||||
|
||||
namespace ambiguous_parse {
|
||||
|
||||
struct AmbiguousParseHelperForIsConstexprConstructible {
|
||||
|
||||
constexpr inline AmbiguousParseHelperForIsConstexprConstructible operator-() { return *this; }
|
||||
|
||||
template<typename T>
|
||||
constexpr inline operator T() {
|
||||
return T{};
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline auto operator -(auto v, AmbiguousParseHelperForIsConstexprConstructible) { return v; }
|
||||
|
||||
}
|
||||
|
||||
#define AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(TYPE_OR_VALUE) [] { ::ams::util::impl::ambiguous_parse::AmbiguousParseHelperForIsConstexprConstructible p; auto v = (TYPE_OR_VALUE)-p; return v; }
|
||||
|
||||
}
|
||||
|
||||
template<typename T, typename...ArgTypes>
|
||||
using is_constexpr_constructible = decltype(impl::IsConstexprConstructibleImpl<T, impl::ConvertToLambdaForIsConstexprConstructible<ArgTypes>()...>(0));
|
||||
|
||||
template<typename T, auto...Args>
|
||||
using is_constexpr_constructible_by_values = decltype(impl::IsConstexprConstructibleImpl<T, impl::ConvertToLambdaForIsConstexprConstructible<Args>()...>(0));
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_1(_1) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_2(_1, _2) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_3(_1, _2, _3) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_4(_1, _2, _3, _4) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_5(_1, _2, _3, _4, _5) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_6(_1, _2, _3, _4, _5, _6) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_7(_1, _2, _3, _4, _5, _6, _7) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_8(_1, _2, _3, _4, _5, _6, _7, _8) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_9(_1, _2, _3, _4, _5, _6, _7, _8, _9) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_11(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_11) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_12(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_11), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_12) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_13(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_11), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_12), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_13) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_14(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_11), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_12), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_13), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_14) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_11), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_12), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_13), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_14), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_15) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
|
||||
(decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_11), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_12), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_13), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_14), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_15), \
|
||||
AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_16) \
|
||||
>(0))::value)
|
||||
|
||||
#define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(...) AMS_VMACRO(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE, __VA_ARGS__)
|
||||
|
||||
#if 0
|
||||
namespace test {
|
||||
|
||||
struct S {
|
||||
private:
|
||||
int m_v;
|
||||
public:
|
||||
S() { }
|
||||
|
||||
constexpr S(int v) : m_v() { }
|
||||
constexpr S(int v, double z) : m_v(v) { }
|
||||
};
|
||||
|
||||
consteval inline int test_constexpr_int() { return 0; }
|
||||
inline int test_not_constexpr_int() { return 0; }
|
||||
|
||||
static_assert(!AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S));
|
||||
static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, int));
|
||||
static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, 0));
|
||||
static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, test_constexpr_int()));
|
||||
static_assert(!AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, test_not_constexpr_int()));
|
||||
|
||||
static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, int, double));
|
||||
static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, int, 0.0));
|
||||
static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, 0, double));
|
||||
static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, 0, 0.0));
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
template<typename T, size_t Size = sizeof(T), size_t Align = alignof(T)>
|
||||
struct TypedStorage {
|
||||
alignas(Align) std::byte _storage[Size];
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static ALWAYS_INLINE T *GetPointer(TypedStorage<T> &ts) {
|
||||
return std::launder(reinterpret_cast<T *>(std::addressof(ts._storage)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static ALWAYS_INLINE const T *GetPointer(const TypedStorage<T> &ts) {
|
||||
return std::launder(reinterpret_cast<const T *>(std::addressof(ts._storage)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static ALWAYS_INLINE T &GetReference(TypedStorage<T> &ts) {
|
||||
return *GetPointer(ts);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static ALWAYS_INLINE const T &GetReference(const TypedStorage<T> &ts) {
|
||||
return *GetPointer(ts);
|
||||
}
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T>
|
||||
static ALWAYS_INLINE T *GetPointerForConstructAt(TypedStorage<T> &ts) {
|
||||
return reinterpret_cast<T *>(std::addressof(ts._storage));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
static ALWAYS_INLINE T *ConstructAt(TypedStorage<T> &ts, Args &&... args) {
|
||||
return std::construct_at(impl::GetPointerForConstructAt(ts), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static ALWAYS_INLINE void DestroyAt(TypedStorage<T> &ts) {
|
||||
return std::destroy_at(GetPointer(ts));
|
||||
}
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T>
|
||||
class TypedStorageGuard {
|
||||
NON_COPYABLE(TypedStorageGuard);
|
||||
private:
|
||||
TypedStorage<T> &m_ts;
|
||||
bool m_active;
|
||||
public:
|
||||
template<typename... Args>
|
||||
ALWAYS_INLINE TypedStorageGuard(TypedStorage<T> &ts, Args &&... args) : m_ts(ts), m_active(true) {
|
||||
ConstructAt(m_ts, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ~TypedStorageGuard() { if (m_active) { DestroyAt(m_ts); } }
|
||||
|
||||
ALWAYS_INLINE void Cancel() { m_active = false; }
|
||||
|
||||
ALWAYS_INLINE TypedStorageGuard(TypedStorageGuard&& rhs) : m_ts(rhs.m_ts), m_active(rhs.m_active) {
|
||||
rhs.Cancel();
|
||||
}
|
||||
|
||||
TypedStorageGuard &operator=(TypedStorageGuard&& rhs) = delete;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
static ALWAYS_INLINE impl::TypedStorageGuard<T> ConstructAtGuarded(TypedStorage<T> &ts, Args &&... args) {
|
||||
return impl::TypedStorageGuard<T>(ts, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
bool VerifyUtf8String(const char *str, size_t size);
|
||||
|
||||
int GetCodePointCountOfUtf8String(const char *str, size_t size);
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util/util_format_string.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
struct Uuid {
|
||||
static constexpr size_t Size = 0x10;
|
||||
static constexpr size_t StringSize = sizeof("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
|
||||
|
||||
u8 data[Size];
|
||||
|
||||
friend bool operator==(const Uuid &lhs, const Uuid &rhs) {
|
||||
return std::memcmp(lhs.data, rhs.data, Size) == 0;
|
||||
}
|
||||
|
||||
friend bool operator!=(const Uuid &lhs, const Uuid &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
const char *ToString(char *dst, size_t dst_size) const {
|
||||
util::SNPrintf(dst, dst_size, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
this->data[ 0], this->data[ 1], this->data[ 2], this->data[ 3], this->data[ 4], this->data[ 5], this->data[ 6], this->data[ 7],
|
||||
this->data[ 8], this->data[ 9], this->data[10], this->data[11], this->data[12], this->data[13], this->data[14], this->data[15]);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
void FromString(const char *str) {
|
||||
char buf[2 + 1] = {};
|
||||
char *end;
|
||||
s32 i = 0;
|
||||
|
||||
for (/* ... */; i < 4; ++i, str += 2) {
|
||||
std::memcpy(buf, str, 2);
|
||||
this->data[i] = static_cast<u8>(std::strtoul(buf, std::addressof(end), 16));
|
||||
}
|
||||
++str;
|
||||
|
||||
for (/* ... */; i < 6; ++i, str += 2) {
|
||||
std::memcpy(buf, str, 2);
|
||||
this->data[i] = static_cast<u8>(std::strtoul(buf, std::addressof(end), 16));
|
||||
}
|
||||
++str;
|
||||
|
||||
for (/* ... */; i < 8; ++i, str += 2) {
|
||||
std::memcpy(buf, str, 2);
|
||||
this->data[i] = static_cast<u8>(std::strtoul(buf, std::addressof(end), 16));
|
||||
}
|
||||
++str;
|
||||
|
||||
for (/* ... */; i < 10; ++i, str += 2) {
|
||||
std::memcpy(buf, str, 2);
|
||||
this->data[i] = static_cast<u8>(std::strtoul(buf, std::addressof(end), 16));
|
||||
}
|
||||
++str;
|
||||
|
||||
for (/* ... */; i < 16; ++i, str += 2) {
|
||||
std::memcpy(buf, str, 2);
|
||||
this->data[i] = static_cast<u8>(std::strtoul(buf, std::addressof(end), 16));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline Uuid InvalidUuid = {};
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
#define AMS_UTIL_VARIADIC_INVOKE_MACRO(__HANDLER__) \
|
||||
__HANDLER__(_01_) \
|
||||
__HANDLER__(_02_) \
|
||||
__HANDLER__(_03_) \
|
||||
__HANDLER__(_04_) \
|
||||
__HANDLER__(_05_) \
|
||||
__HANDLER__(_06_) \
|
||||
__HANDLER__(_07_) \
|
||||
__HANDLER__(_08_) \
|
||||
__HANDLER__(_09_) \
|
||||
__HANDLER__(_0A_) \
|
||||
__HANDLER__(_0B_) \
|
||||
__HANDLER__(_0C_) \
|
||||
__HANDLER__(_0D_) \
|
||||
__HANDLER__(_0E_) \
|
||||
__HANDLER__(_0F_)
|
||||
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_01_(_T_) typename _T_##_01_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_02_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_01_(_T_), typename _T_##_02_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_03_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_02_(_T_), typename _T_##_03_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_04_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_03_(_T_), typename _T_##_04_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_05_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_04_(_T_), typename _T_##_05_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_06_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_05_(_T_), typename _T_##_06_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_07_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_06_(_T_), typename _T_##_07_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_08_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_07_(_T_), typename _T_##_08_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_09_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_08_(_T_), typename _T_##_09_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0A_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_09_(_T_), typename _T_##_0A_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0B_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0A_(_T_), typename _T_##_0B_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0C_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0B_(_T_), typename _T_##_0C_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0D_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0C_(_T_), typename _T_##_0D_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0E_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0D_(_T_), typename _T_##_0E_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0F_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0E_(_T_), typename _T_##_0F_
|
||||
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_01_(_T_, _N_) _T_##_01_ &&_N_##_01_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_02_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_01_(_T_, _N_), _T_##_02_ &&_N_##_02_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_03_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_02_(_T_, _N_), _T_##_03_ &&_N_##_03_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_04_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_03_(_T_, _N_), _T_##_04_ &&_N_##_04_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_05_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_04_(_T_, _N_), _T_##_05_ &&_N_##_05_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_06_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_05_(_T_, _N_), _T_##_06_ &&_N_##_06_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_07_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_06_(_T_, _N_), _T_##_07_ &&_N_##_07_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_08_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_07_(_T_, _N_), _T_##_08_ &&_N_##_08_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_09_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_08_(_T_, _N_), _T_##_09_ &&_N_##_09_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0A_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_09_(_T_, _N_), _T_##_0A_ &&_N_##_0A_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0B_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0A_(_T_, _N_), _T_##_0B_ &&_N_##_0B_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0C_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0B_(_T_, _N_), _T_##_0C_ &&_N_##_0C_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0D_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0C_(_T_, _N_), _T_##_0D_ &&_N_##_0D_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0E_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0D_(_T_, _N_), _T_##_0E_ &&_N_##_0E_
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0F_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0E_(_T_, _N_), _T_##_0F_ &&_N_##_0F_
|
||||
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_01_(_T_, _N_) ::std::forward<_T_##_01_>(_N_##_01_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_02_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_01_(_T_, _N_), ::std::forward<_T_##_02_>(_N_##_02_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_03_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_02_(_T_, _N_), ::std::forward<_T_##_03_>(_N_##_03_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_04_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_03_(_T_, _N_), ::std::forward<_T_##_04_>(_N_##_04_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_05_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_04_(_T_, _N_), ::std::forward<_T_##_05_>(_N_##_05_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_06_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_05_(_T_, _N_), ::std::forward<_T_##_06_>(_N_##_06_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_07_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_06_(_T_, _N_), ::std::forward<_T_##_07_>(_N_##_07_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_08_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_07_(_T_, _N_), ::std::forward<_T_##_08_>(_N_##_08_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_09_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_08_(_T_, _N_), ::std::forward<_T_##_09_>(_N_##_09_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0A_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_09_(_T_, _N_), ::std::forward<_T_##_0A_>(_N_##_0A_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0B_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0A_(_T_, _N_), ::std::forward<_T_##_0B_>(_N_##_0B_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0C_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0B_(_T_, _N_), ::std::forward<_T_##_0C_>(_N_##_0C_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0D_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0C_(_T_, _N_), ::std::forward<_T_##_0D_>(_N_##_0D_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0E_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0D_(_T_, _N_), ::std::forward<_T_##_0E_>(_N_##_0E_)
|
||||
#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0F_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0E_(_T_, _N_), ::std::forward<_T_##_0F_>(_N_##_0F_)
|
||||
Reference in New Issue
Block a user