ams: support building unit test programs on windows/linux/macos
This commit is contained in:
@@ -52,7 +52,9 @@ namespace ams::util {
|
||||
template<UsableAtomicType T>
|
||||
using AtomicStorage = typename AtomicIntegerStorage<T>::Type;
|
||||
|
||||
static_assert(std::same_as<AtomicStorage<void *>, uintptr_t>);
|
||||
|
||||
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>);
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace ams::util {
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
ALWAYS_INLINE void Store(T arg) {
|
||||
return m_v.store(Order);
|
||||
return m_v.store(arg, Order);
|
||||
}
|
||||
|
||||
template<std::memory_order Order = std::memory_order_seq_cst>
|
||||
@@ -135,6 +135,8 @@ namespace ams::util {
|
||||
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);
|
||||
@@ -218,6 +220,7 @@ namespace ams::util {
|
||||
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
|
||||
|
||||
|
||||
}
|
||||
@@ -49,32 +49,32 @@ namespace ams::util {
|
||||
}
|
||||
|
||||
template<>
|
||||
constexpr ALWAYS_INLINE void *AlignUp<void *>(void *value, size_t alignment) {
|
||||
ALWAYS_INLINE void *AlignUp<void *>(void *value, size_t alignment) {
|
||||
return reinterpret_cast<void *>(AlignUp(reinterpret_cast<uintptr_t>(value), alignment));
|
||||
}
|
||||
|
||||
template<>
|
||||
constexpr ALWAYS_INLINE const void *AlignUp<const void *>(const void *value, size_t alignment) {
|
||||
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<>
|
||||
constexpr ALWAYS_INLINE void *AlignDown<void *>(void *value, size_t alignment) {
|
||||
ALWAYS_INLINE void *AlignDown<void *>(void *value, size_t alignment) {
|
||||
return reinterpret_cast<void *>(AlignDown(reinterpret_cast<uintptr_t>(value), alignment));
|
||||
}
|
||||
|
||||
template<>
|
||||
constexpr ALWAYS_INLINE const void *AlignDown<const void *>(const void *value, size_t alignment) {
|
||||
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<>
|
||||
constexpr ALWAYS_INLINE bool IsAligned<void *>(void *value, size_t alignment) {
|
||||
ALWAYS_INLINE bool IsAligned<void *>(void *value, size_t alignment) {
|
||||
return IsAligned(reinterpret_cast<uintptr_t>(value), alignment);
|
||||
}
|
||||
|
||||
template<>
|
||||
constexpr ALWAYS_INLINE bool IsAligned<const void *>(const void *value, size_t alignment) {
|
||||
ALWAYS_INLINE bool IsAligned<const void *>(const void *value, size_t alignment) {
|
||||
return IsAligned(reinterpret_cast<uintptr_t>(value), alignment);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,143 @@ namespace ams::util {
|
||||
CharacterEncodingResult_InvalidFormat = 2,
|
||||
};
|
||||
|
||||
CharacterEncodingResult ConvertCharacterUtf8ToUtf32(u32 *dst, const char *src);
|
||||
namespace impl {
|
||||
|
||||
CharacterEncodingResult PickOutCharacterFromUtf8String(char *dst, const char **str);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O3")
|
||||
AMS_PRAGMA_BEGIN_OPTIMIZE_O3()
|
||||
|
||||
/* Forward declare implementation class for Node. */
|
||||
namespace impl {
|
||||
@@ -627,6 +626,6 @@ namespace ams::util {
|
||||
}
|
||||
};
|
||||
|
||||
#pragma GCC pop_options
|
||||
AMS_PRAGMA_END_OPTIMIZE()
|
||||
|
||||
}
|
||||
@@ -22,8 +22,7 @@
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O3")
|
||||
AMS_PRAGMA_BEGIN_OPTIMIZE_O3()
|
||||
|
||||
namespace impl {
|
||||
|
||||
@@ -583,6 +582,6 @@ namespace ams::util {
|
||||
}
|
||||
};
|
||||
|
||||
#pragma GCC pop_options
|
||||
AMS_PRAGMA_END_OPTIMIZE()
|
||||
|
||||
}
|
||||
|
||||
@@ -124,17 +124,47 @@ namespace ams::util {
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,18 @@ namespace ams::util {
|
||||
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);
|
||||
@@ -101,4 +113,50 @@ namespace ams::util {
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user