ams: support building unit test programs on windows/linux/macos

This commit is contained in:
Michael Scire
2022-03-06 12:08:20 -08:00
committed by SciresM
parent 9a38be201a
commit 64a97576d0
756 changed files with 33359 additions and 9372 deletions

View File

@@ -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>);

View File

@@ -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
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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));
}

View File

@@ -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
}