diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf357aa59..9b6bc3418 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,25 +60,35 @@ jobs: } - { - name: "Ubuntu Clang-17 Sanitizer", + name: "Ubuntu Clang-18 Debug", os: ubuntu-latest, - build_type: "Release", - cc: "clang-17", - cxx: "clang++-17", - clang_version: 17, + build_type: "Debug", + cc: "clang-18", + cxx: "clang++-18", + clang_version: 18, installed_clang_version: 14 } - - { - name: "Ubuntu Clang-18 Debug", + name: "Ubuntu Clang-18 Sanitizer", os: ubuntu-latest, - build_type: "Debug", + build_type: "Release", cc: "clang-18", cxx: "clang++-18", clang_version: 18, installed_clang_version: 14 } + + - { + name: "Ubuntu Clang-19 Debug", + os: ubuntu-latest, + build_type: "Debug", + cc: "clang-19", + cxx: "clang++-19", + clang_version: 19, + installed_clang_version: 14 + } + - { name: "Ubuntu GCC", os: ubuntu-latest, diff --git a/.github/workflows/try.yml b/.github/workflows/try.yml index 1975ea915..e9cf80c0c 100644 --- a/.github/workflows/try.yml +++ b/.github/workflows/try.yml @@ -62,25 +62,34 @@ jobs: } - { - name: "Ubuntu Clang-17 Sanitizer", + name: "Ubuntu Clang-18 Debug", os: ubuntu-latest, - build_type: "Release", - cc: "clang-17", - cxx: "clang++-17", - clang_version: 17, + build_type: "Debug", + cc: "clang-18", + cxx: "clang++-18", + clang_version: 18, installed_clang_version: 14 } - - { - name: "Ubuntu Clang-18 Debug", + name: "Ubuntu Clang-18 Sanitizer", os: ubuntu-latest, - build_type: "Debug", + build_type: "Release", cc: "clang-18", cxx: "clang++-18", clang_version: 18, installed_clang_version: 14 } + - { + name: "Ubuntu Clang-19 Debug", + os: ubuntu-latest, + build_type: "Debug", + cc: "clang-19", + cxx: "clang++-19", + clang_version: 19, + installed_clang_version: 14 + } + - { name: "Ubuntu GCC", os: ubuntu-latest, diff --git a/sus/num/__private/intrinsics.h b/sus/num/__private/intrinsics.h index f6ec7435c..0d1ed4ded 100644 --- a/sus/num/__private/intrinsics.h +++ b/sus/num/__private/intrinsics.h @@ -25,7 +25,7 @@ #include #if _MSC_VER -#include +# include #endif #include "sus/assertions/unreachable.h" @@ -75,49 +75,57 @@ __sus_pure_const _sus_always_inline constexpr T unchecked_not(T x) noexcept { template requires(std::is_integral_v) -__sus_pure_const _sus_always_inline constexpr T unchecked_add(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T unchecked_add(T x, + T y) noexcept { return static_cast(MathType{x} + MathType{y}); } template requires(std::is_integral_v) -__sus_pure_const _sus_always_inline constexpr T unchecked_sub(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T unchecked_sub(T x, + T y) noexcept { return static_cast(MathType{x} - MathType{y}); } template requires(std::is_integral_v) -__sus_pure_const _sus_always_inline constexpr T unchecked_mul(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T unchecked_mul(T x, + T y) noexcept { return static_cast(MathType{x} * MathType{y}); } template requires(std::is_integral_v) -__sus_pure_const _sus_always_inline constexpr T unchecked_div(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T unchecked_div(T x, + T y) noexcept { return static_cast(MathType{x} / MathType{y}); } template requires(std::is_integral_v) -__sus_pure_const _sus_always_inline constexpr T unchecked_rem(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T unchecked_rem(T x, + T y) noexcept { return static_cast(MathType{x} % MathType{y}); } template requires(std::is_integral_v) -__sus_pure_const _sus_always_inline constexpr T unchecked_and(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T unchecked_and(T x, + T y) noexcept { return static_cast(MathType{x} & MathType{y}); } template requires(std::is_integral_v) -__sus_pure_const _sus_always_inline constexpr T unchecked_or(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T unchecked_or(T x, + T y) noexcept { return static_cast(MathType{x} | MathType{y}); } template requires(std::is_integral_v) -__sus_pure_const _sus_always_inline constexpr T unchecked_xor(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T unchecked_xor(T x, + T y) noexcept { return static_cast(MathType{x} ^ MathType{y}); } @@ -541,39 +549,39 @@ __sus_pure_const _sus_always_inline constexpr uint32_t leading_zeros_nonzero( #if _MSC_VER if constexpr (::sus::mem::size_of() == 8u) { -#if 1 +# if 1 unsigned long index; _BitScanReverse64(&index, value); return static_cast(63ul ^ index); -#else +# else // TODO: Enable this when target CPU is appropriate: // - AMD: Advanced Bit Manipulation (ABM) // - Intel: Haswell // TODO: On Arm ARMv5T architecture and later use `_arm_clz` return static_cast(__lzcnt64(&count, int64_t{value})); -#endif +# endif } else if constexpr (::sus::mem::size_of() == 4u) { -#if 1 +# if 1 unsigned long index; _BitScanReverse(&index, uint32_t{value}); return static_cast(31ul ^ index); -#else +# else // TODO: Enable this when target CPU is appropriate: // - AMD: Advanced Bit Manipulation (ABM) // - Intel: Haswell // TODO: On Arm ARMv5T architecture and later use `_arm_clz` return __lzcnt(&count, uint32_t{value}); -#endif +# endif } else { static_assert(::sus::mem::size_of() <= 2u); -#if 1 +# if 1 unsigned long index; _BitScanReverse(&index, uint32_t{value}); return static_cast( (31ul ^ index) - ((::sus::mem::size_of() - ::sus::mem::size_of()) * 8u)); -#else +# else // TODO: Enable this when target CPU is appropriate: // - AMD: Advanced Bit Manipulation (ABM) // - Intel: Haswell @@ -582,7 +590,7 @@ __sus_pure_const _sus_always_inline constexpr uint32_t leading_zeros_nonzero( __lzcnt16(&count, uint16_t{value}) - ((::sus::mem::size_of() - ::sus::mem::size_of()) * 8u)); -#endif +# endif } #else if constexpr (::sus::mem::size_of() <= @@ -733,26 +741,26 @@ __sus_pure_const _sus_always_inline constexpr T swap_bytes(T value) noexcept { if constexpr (::sus::mem::size_of() == 1) { return value; } else if constexpr (::sus::mem::size_of() == 2) { - unsigned char a = (value >> 0) & 0xff; - unsigned char b = (value >> 8) & 0xff; - return (a << 8) | (b << 0); + MathType a = (MathType(value) >> 0) & MathType{0xff}; + MathType b = (MathType(value) >> 8) & MathType{0xff}; + return static_cast((a << 8) | (b << 0)); } else if constexpr (::sus::mem::size_of() == 4) { - unsigned char a = (value >> 0) & 0xff; - unsigned char b = (value >> 8) & 0xff; - unsigned char c = (value >> 16) & 0xff; - unsigned char d = (value >> 24) & 0xff; + T a = (value >> 0) & T{0xff}; + T b = (value >> 8) & T{0xff}; + T c = (value >> 16) & T{0xff}; + T d = (value >> 24) & T{0xff}; return (a << 24) | (b << 16) | (c << 8) | (d << 0); } else if constexpr (::sus::mem::size_of() == 8) { - unsigned char a = (value >> 0) & 0xff; - unsigned char b = (value >> 8) & 0xff; - unsigned char c = (value >> 16) & 0xff; - unsigned char d = (value >> 24) & 0xff; - unsigned char e = (value >> 32) & 0xff; - unsigned char f = (value >> 40) & 0xff; - unsigned char g = (value >> 48) & 0xff; - unsigned char h = (value >> 56) & 0xff; - return (a << 24) | (b << 16) | (c << 8) | (d << 0) | (e << 24) | - (f << 16) | (g << 8) | (h << 0); + T a = (value >> 0) & T{0xff}; + T b = (value >> 8) & T{0xff}; + T c = (value >> 16) & T{0xff}; + T d = (value >> 24) & T{0xff}; + T e = (value >> 32) & T{0xff}; + T f = (value >> 40) & T{0xff}; + T g = (value >> 48) & T{0xff}; + T h = (value >> 56) & T{0xff}; + return (a << 56) | (b << 48) | (c << 40) | (d << 32) | // + (e << 24) | (f << 16) | (g << 8) | (h << 0); } } @@ -853,8 +861,8 @@ __sus_pure_const _sus_always_inline constexpr bool sign_bit(T x) noexcept { template requires(std::is_integral_v && !std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const inline constexpr OverflowOut add_with_overflow(T x, - T y) noexcept { +__sus_pure_const inline constexpr OverflowOut add_with_overflow( + T x, T y) noexcept { return OverflowOut sus_clang_bug_56394(){ .overflow = x > max_value() - y, .value = unchecked_add(x, y), @@ -864,8 +872,8 @@ __sus_pure_const inline constexpr OverflowOut add_with_overflow(T x, template requires(std::is_integral_v && std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const inline constexpr OverflowOut add_with_overflow(T x, - T y) noexcept { +__sus_pure_const inline constexpr OverflowOut add_with_overflow( + T x, T y) noexcept { const auto out = into_signed(unchecked_add(into_unsigned(x), into_unsigned(y))); return OverflowOut sus_clang_bug_56394(){ @@ -903,8 +911,8 @@ __sus_pure_const inline constexpr OverflowOut add_with_overflow_unsigned( template requires(std::is_integral_v && !std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const inline constexpr OverflowOut sub_with_overflow(T x, - T y) noexcept { +__sus_pure_const inline constexpr OverflowOut sub_with_overflow( + T x, T y) noexcept { return OverflowOut sus_clang_bug_56394(){ .overflow = x < unchecked_add(min_value(), y), .value = unchecked_sub(x, y), @@ -914,8 +922,8 @@ __sus_pure_const inline constexpr OverflowOut sub_with_overflow(T x, template requires(std::is_integral_v && std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const inline constexpr OverflowOut sub_with_overflow(T x, - T y) noexcept { +__sus_pure_const inline constexpr OverflowOut sub_with_overflow( + T x, T y) noexcept { const auto out = into_signed(unchecked_sub(into_unsigned(x), into_unsigned(y))); return OverflowOut sus_clang_bug_56394(){ @@ -948,8 +956,8 @@ sub_with_unsigned_positive_result(T x, T y) noexcept { template requires(std::is_integral_v && !std::is_signed_v && ::sus::mem::size_of() <= 4) -__sus_pure_const inline constexpr OverflowOut mul_with_overflow(T x, - T y) noexcept { +__sus_pure_const inline constexpr OverflowOut mul_with_overflow( + T x, T y) noexcept { // TODO: Can we use compiler intrinsics? auto out = unchecked_mul(into_widened(x), into_widened(y)); using Wide = decltype(out); @@ -960,8 +968,8 @@ __sus_pure_const inline constexpr OverflowOut mul_with_overflow(T x, template requires(std::is_integral_v && !std::is_signed_v && ::sus::mem::size_of() == 8) -__sus_pure_const inline constexpr OverflowOut mul_with_overflow(T x, - T y) noexcept { +__sus_pure_const inline constexpr OverflowOut mul_with_overflow( + T x, T y) noexcept { #if _MSC_VER if (std::is_constant_evaluated()) { const bool overflow = @@ -987,8 +995,8 @@ __sus_pure_const inline constexpr OverflowOut mul_with_overflow(T x, template requires(std::is_integral_v && std::is_signed_v && ::sus::mem::size_of() <= 4) -__sus_pure_const inline constexpr OverflowOut mul_with_overflow(T x, - T y) noexcept { +__sus_pure_const inline constexpr OverflowOut mul_with_overflow( + T x, T y) noexcept { // TODO: Can we use compiler intrinsics? auto out = into_widened(x) * into_widened(y); using Wide = decltype(out); @@ -1000,8 +1008,8 @@ __sus_pure_const inline constexpr OverflowOut mul_with_overflow(T x, template requires(std::is_integral_v && std::is_signed_v && ::sus::mem::size_of() == 8) -__sus_pure_const inline constexpr OverflowOut mul_with_overflow(T x, - T y) noexcept { +__sus_pure_const inline constexpr OverflowOut mul_with_overflow( + T x, T y) noexcept { #if _MSC_VER if (x == T{0} || y == T{0}) return OverflowOut sus_clang_bug_56394(){.overflow = false, @@ -1228,14 +1236,16 @@ __sus_pure_const inline constexpr T saturating_mul(T x, T y) noexcept { template requires(std::is_integral_v && !std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const _sus_always_inline constexpr T wrapping_add(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T wrapping_add(T x, + T y) noexcept { return x + y; } template requires(std::is_integral_v && std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const _sus_always_inline constexpr T wrapping_add(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T wrapping_add(T x, + T y) noexcept { // TODO: Are there cheaper intrinsics? return add_with_overflow(x, y).value; } @@ -1243,14 +1253,16 @@ __sus_pure_const _sus_always_inline constexpr T wrapping_add(T x, T y) noexcept template requires(std::is_integral_v && !std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const _sus_always_inline constexpr T wrapping_sub(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T wrapping_sub(T x, + T y) noexcept { return unchecked_sub(x, y); } template requires(std::is_integral_v && std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const _sus_always_inline constexpr T wrapping_sub(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T wrapping_sub(T x, + T y) noexcept { // TODO: Are there cheaper intrinsics? return sub_with_overflow(x, y).value; } @@ -1258,14 +1270,16 @@ __sus_pure_const _sus_always_inline constexpr T wrapping_sub(T x, T y) noexcept template requires(std::is_integral_v && !std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const _sus_always_inline constexpr T wrapping_mul(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T wrapping_mul(T x, + T y) noexcept { return x * y; } template requires(std::is_integral_v && std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const _sus_always_inline constexpr T wrapping_mul(T x, T y) noexcept { +__sus_pure_const _sus_always_inline constexpr T wrapping_mul(T x, + T y) noexcept { // TODO: Are there cheaper intrinsics? return mul_with_overflow(x, y).value; } @@ -1328,7 +1342,8 @@ __sus_pure_const inline constexpr bool div_overflows(T x, T y) noexcept { template requires(std::is_integral_v && std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const inline constexpr bool div_overflows_nonzero(T x, T y) noexcept { +__sus_pure_const inline constexpr bool div_overflows_nonzero(T x, + T y) noexcept { // Using `&` helps LLVM see that it is the same check made in division. return ((x == min_value()) & (y == T{-1})); } @@ -1337,8 +1352,8 @@ __sus_pure_const inline constexpr bool div_overflows_nonzero(T x, T y) noexcept template requires(std::is_integral_v && std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const inline constexpr T div_euclid(::sus::marker::UnsafeFnMarker, T x, - T y) noexcept { +__sus_pure_const inline constexpr T div_euclid(::sus::marker::UnsafeFnMarker, + T x, T y) noexcept { const auto q = unchecked_div(x, y); if (x % y >= 0) return q; @@ -1352,8 +1367,8 @@ __sus_pure_const inline constexpr T div_euclid(::sus::marker::UnsafeFnMarker, T template requires(std::is_integral_v && std::is_signed_v && ::sus::mem::size_of() <= 8) -__sus_pure_const inline constexpr T rem_euclid(::sus::marker::UnsafeFnMarker, T x, - T y) noexcept { +__sus_pure_const inline constexpr T rem_euclid(::sus::marker::UnsafeFnMarker, + T x, T y) noexcept { const auto r = unchecked_rem(x, y); if (r < 0) { if (y < 0) @@ -1529,18 +1544,19 @@ __sus_pure_const _sus_always_inline constexpr int32_t exponent_bits( } /// This function requires that `x` is a normal value to produce a value result. -__sus_pure_const _sus_always_inline constexpr int32_t float_normal_exponent_value( - float x) noexcept { +__sus_pure_const _sus_always_inline constexpr int32_t +float_normal_exponent_value(float x) noexcept { return exponent_bits(x) - int32_t{127}; } /// This function requires that `x` is a normal value to produce a value result. -__sus_pure_const _sus_always_inline constexpr int32_t float_normal_exponent_value( - double x) noexcept { +__sus_pure_const _sus_always_inline constexpr int32_t +float_normal_exponent_value(double x) noexcept { return exponent_bits(x) - int32_t{1023}; } -__sus_pure_const _sus_always_inline constexpr uint32_t mantissa(float x) noexcept { +__sus_pure_const _sus_always_inline constexpr uint32_t mantissa( + float x) noexcept { constexpr uint32_t mask = 0b00000000011111111111111111111111; return into_unsigned_integer(x) & mask; } @@ -1841,7 +1857,8 @@ __sus_pure_const constexpr inline Out static_cast_int_to_float(T x) noexcept { template requires(std::is_floating_point_v && std::is_floating_point_v && ::sus::mem::size_of() == 8 && ::sus::mem::size_of() == 4) -__sus_pure_const constexpr inline Out static_cast_to_smaller_float(T x) noexcept { +__sus_pure_const constexpr inline Out static_cast_to_smaller_float( + T x) noexcept { if (x <= T{max_value()} && x >= T{min_value()}) [[likely]] { // C++20 Section 7.3.9: A prvalue of floating-point type can be converted to // a prvalue of another floating-point type. If the source value can be diff --git a/sus/num/__private/unsigned_integer_methods_impl.inc b/sus/num/__private/unsigned_integer_methods_impl.inc index 5f169cff0..504f916fd 100644 --- a/sus/num/__private/unsigned_integer_methods_impl.inc +++ b/sus/num/__private/unsigned_integer_methods_impl.inc @@ -734,10 +734,10 @@ _sus_pure constexpr _self _self::from_ne_bytes( _self val; if (std::is_constant_evaluated()) { val = _primitive{0}; - for (usize i; i < ::sus::mem::size_of<_primitive>(); i += 1u) { - // size_of<_primitive>() is < u32::MAX so the cast() is not lossy. - val |= bytes[i] << ::sus::cast(::sus::mem::size_of<_primitive>() - - 1u - i); + // size_of<_primitive>() < u32::MAX` so casting to u32 is not lossy. + for (u32 i; i < ::sus::cast(::sus::mem::size_of<_primitive>()); + i += 1u) { + val |= _primitive{bytes[i]} << (i * 8u); } } else { ::sus::ptr::copy_nonoverlapping( diff --git a/sus/num/u16_unittest.cc b/sus/num/u16_unittest.cc index 7df91cbfb..92c8f6738 100644 --- a/sus/num/u16_unittest.cc +++ b/sus/num/u16_unittest.cc @@ -745,4 +745,135 @@ TEST(u16, fmt) { EXPECT_EQ(fmt::format("{:#x}", 12345_u16), "0x3039"); } +TEST(u16, ToBe) { + if constexpr (std::endian::native == std::endian::little) { + constexpr auto a = (0x1234_u16).to_be(); + EXPECT_EQ(a, 0x3412_u16); + + EXPECT_EQ((0x1234_u16).to_be(), 0x3412_u16); + EXPECT_EQ((0_u16).to_be(), 0_u16); + EXPECT_EQ((1_u16 << 15_u32).to_be(), (1_u16 << 7_u32)); + } else { + constexpr auto a = (0x1234_u16).to_be(); + EXPECT_EQ(a, 0x1234_u16); + + EXPECT_EQ((0x1234_u16).to_be(), 0x1234_u16); + EXPECT_EQ((0_u16).to_be(), 0_u16); + EXPECT_EQ((1_u16 << 15_u32).to_be(), (1_u16 << 15_u32)); + } +} + +TEST(u16, FromBe) { + if constexpr (std::endian::native == std::endian::little) { + constexpr auto a = u16::from_be(0x1234_u16); + EXPECT_EQ(a, 0x3412_u16); + + EXPECT_EQ(u16::from_be(0x1234_u16), 0x3412_u16); + EXPECT_EQ(u16::from_be(0_u16), 0_u16); + EXPECT_EQ(u16::from_be(1_u16 << 15_u32), 1_u16 << 7_u32); + } else { + constexpr auto a = u16::from_be(0x1234_u16); + EXPECT_EQ(a, 0x1234_u16); + + EXPECT_EQ(u16::from_be(0x1234_u16), 0x1234_u16); + EXPECT_EQ(u16::from_be(0_u16), 0_u16); + EXPECT_EQ(u16::from_be(1_u16 << 15_u32), 1_u16 << 15_u32); + } +} + +TEST(u16, ToLe) { + if constexpr (std::endian::native == std::endian::big) { + constexpr auto a = (0x1234_u16).to_le(); + EXPECT_EQ(a, 0x3412_u16); + + EXPECT_EQ((0x1234_u16).to_le(), 0x3412_u16); + EXPECT_EQ((0_u16).to_le(), 0_u16); + EXPECT_EQ(u16::MIN.to_le(), 0x80_u16); + } else { + constexpr auto a = (0x1234_u16).to_le(); + EXPECT_EQ(a, 0x1234_u16); + + EXPECT_EQ((0x1234_u16).to_le(), 0x1234_u16); + EXPECT_EQ((0_u16).to_le(), 0_u16); + EXPECT_EQ(u16::MIN.to_le(), u16::MIN); + } +} + +TEST(u16, FromLe) { + if constexpr (std::endian::native == std::endian::big) { + constexpr auto a = u16::from_le(0x1234_u16); + EXPECT_EQ(a, 0x3412_u16); + + EXPECT_EQ(u16::from_le(0x1234_u16), 0x3412_u16); + EXPECT_EQ(u16::from_le(0_u16), 0_u16); + EXPECT_EQ(u16::from_le(u16::MIN), 0x80_u16); + } else { + constexpr auto a = u16::from_le(0x1234_u16); + EXPECT_EQ(a, 0x1234_u16); + + EXPECT_EQ(u16::from_le(0x1234_u16), 0x1234_u16); + EXPECT_EQ(u16::from_le(0_u16), 0_u16); + EXPECT_EQ(u16::from_le(u16::MIN), u16::MIN); + } +} + +TEST(u16, ToBeBytes) { + { + constexpr auto a = (0x1234_u16).to_be_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8, 0x34_u8))); + } + { + auto a = (0x1234_u16).to_be_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8, 0x34_u8))); + } +} + +TEST(u16, FromBeBytes) { + constexpr auto bytes = sus::Array(0x12_u8, 0x34_u8); + if constexpr (std::endian::native == std::endian::little) { + EXPECT_EQ(u16::from_be_bytes(bytes), 0x12'34u); + } else { + EXPECT_EQ(u16::from_be_bytes(bytes), 0x34'12u); + } + static_assert(std::same_as); + + static_assert(std::endian::native != std::endian::little || + u16::from_be_bytes(bytes) == 0x12'34u); + static_assert(std::endian::native != std::endian::big || + u16::from_be_bytes(bytes) == 0x34'12u); +} + +TEST(u16, ToLeBytes) { + { + constexpr auto a = (0x1234_u16).to_le_bytes(); + EXPECT_EQ(a, (sus::Array(0x34_u8, 0x12_u8))); + } + { + auto a = (0x1234_u16).to_le_bytes(); + EXPECT_EQ(a, (sus::Array(0x34_u8, 0x12_u8))); + } +} + +TEST(u16, ToNeBytes) { + if constexpr (std::endian::native == std::endian::big) { + { + constexpr auto a = (0x1234_u16).to_ne_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8, 0x34_u8))); + } + { + auto a = (0x1234_u16).to_ne_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8, 0x34_u8))); + } + } else { + { + constexpr auto a = (0x1234_u16).to_ne_bytes(); + EXPECT_EQ(a, (sus::Array(0x34_u8, 0x12_u8))); + } + { + auto a = (0x1234_u16).to_ne_bytes(); + EXPECT_EQ(a, (sus::Array(0x34_u8, 0x12_u8))); + } + } +} + } // namespace diff --git a/sus/num/u32_unittest.cc b/sus/num/u32_unittest.cc index 7e41f8e7b..90d98b8f2 100644 --- a/sus/num/u32_unittest.cc +++ b/sus/num/u32_unittest.cc @@ -1872,6 +1872,21 @@ TEST(u32, ToBeBytes) { } } +TEST(u32, FromBeBytes) { + constexpr auto bytes = sus::Array(0x12_u8, 0x34_u8, 0x56_u8, 0x78_u8); + if constexpr (std::endian::native == std::endian::little) { + EXPECT_EQ(u32::from_be_bytes(bytes), 0x12'34'56'78u); + } else { + EXPECT_EQ(u32::from_be_bytes(bytes), 0x78'56'34'12u); + } + static_assert(std::same_as); + + static_assert(std::endian::native != std::endian::little || + u32::from_be_bytes(bytes) == 0x12'34'56'78u); + static_assert(std::endian::native != std::endian::big || + u32::from_be_bytes(bytes) == 0x78'56'34'12u); +} + TEST(u32, ToLeBytes) { { constexpr auto a = (0x12345678_u32).to_le_bytes(); diff --git a/sus/num/u64_unittest.cc b/sus/num/u64_unittest.cc index 8b84c309c..92b4a806e 100644 --- a/sus/num/u64_unittest.cc +++ b/sus/num/u64_unittest.cc @@ -15,14 +15,14 @@ #include #include "googletest/include/gtest/gtest.h" +#include "sus/cmp/eq.h" +#include "sus/cmp/ord.h" #include "sus/collections/array.h" #include "sus/construct/into.h" #include "sus/iter/__private/step.h" #include "sus/num/num_concepts.h" #include "sus/num/signed_integer.h" #include "sus/num/unsigned_integer.h" -#include "sus/cmp/eq.h" -#include "sus/cmp/ord.h" #include "sus/prelude.h" #include "sus/tuple/tuple.h" @@ -761,4 +761,144 @@ TEST(u64, fmt) { EXPECT_EQ(fmt::format("{:#x}", 123456789_u64), "0x75bcd15"); } +TEST(u64, ToBe) { + if constexpr (std::endian::native == std::endian::little) { + constexpr auto a = (0x12345678'90123456_u64).to_be(); + EXPECT_EQ(a, 0x56341290'78563412_u64); + + EXPECT_EQ((0x12345678'90123456_u64).to_be(), 0x56341290'78563412_u64); + EXPECT_EQ((0_u64).to_be(), 0_u32); + EXPECT_EQ((1_u64 << 63_u32).to_be(), (1_u64 << 7_u32)); + } else { + constexpr auto a = (0x12345678'90123456_u64).to_be(); + EXPECT_EQ(a, 0x12345678'90123456_u64); + + EXPECT_EQ((0x12345678'90123456_u64).to_be(), 0x12345678'90123456_u64); + EXPECT_EQ((0_u64).to_be(), 0_u32); + EXPECT_EQ((1_u64 << 63_u32).to_be(), (1_u64 << 63_u32)); + } +} + +TEST(u64, FromBe) { + if constexpr (std::endian::native == std::endian::little) { + constexpr auto a = u64::from_be(0x12345678'90123456_u64); + EXPECT_EQ(a, 0x56341290'78563412_u64); + + EXPECT_EQ(u64::from_be(0x12345678'90123456_u64), 0x56341290'78563412_u64); + EXPECT_EQ(u64::from_be(0_u64), 0_u64); + EXPECT_EQ(u64::from_be(1_u64 << 63_u32), 1_u64 << 7_u32); + } else { + constexpr auto a = u64::from_be(0x12345678'90123456_u64); + EXPECT_EQ(a, 0x12345678'90123456_u64); + + EXPECT_EQ(u64::from_be(0x12345678'90123456_u64), 0x12345678'90123456_u64); + EXPECT_EQ(u64::from_be(0_u32), 0_u32); + EXPECT_EQ(u64::from_be(1_u64 << 63_u32), 1_u64 << 63_u32); + } +} + +TEST(u64, ToLe) { + if constexpr (std::endian::native == std::endian::big) { + constexpr auto a = (0x12345678'90123456_u64).to_le(); + EXPECT_EQ(a, 0x56341290'78563412_u64); + + EXPECT_EQ((0x12345678'90123456_u64).to_le(), 0x56341290'78563412_u64); + EXPECT_EQ((0_u64).to_le(), 0_u64); + EXPECT_EQ(u64::MIN.to_le(), 0x80_u32); + } else { + constexpr auto a = (0x12345678'90123456_u64).to_le(); + EXPECT_EQ(a, 0x12345678'90123456_u64); + + EXPECT_EQ((0x12345678'90123456_u64).to_le(), 0x12345678'90123456_u64); + EXPECT_EQ((0_u64).to_le(), 0_u64); + EXPECT_EQ(u64::MIN.to_le(), u64::MIN); + } +} + +TEST(u64, FromLe) { + if constexpr (std::endian::native == std::endian::big) { + constexpr auto a = u64::from_le(0x12345678'90123456_u64); + EXPECT_EQ(a, 0x56341290'78563412_u64); + + EXPECT_EQ(u64::from_le(0x12345678'90123456_u64), 0x56341290'78563412_u64); + EXPECT_EQ(u64::from_le(0_u64), 0_u64); + EXPECT_EQ(u64::from_le(u64::MIN), 0x80_u64); + } else { + constexpr auto a = u64::from_le(0x12345678'90123456_u64); + EXPECT_EQ(a, 0x12345678'90123456_u64); + + EXPECT_EQ(u64::from_le(0x12345678'90123456_u64), 0x12345678'90123456_u64); + EXPECT_EQ(u64::from_le(0_u64), 0_u64); + EXPECT_EQ(u64::from_le(u64::MIN), u64::MIN); + } +} + +TEST(u64, ToBeBytes) { + { + constexpr auto a = (0x12345678'90123456_u64).to_be_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8, 0x34_u8, 0x56_u8, 0x78_u8, 0x90_u8, + 0x12_u8, 0x34_u8, 0x56_u8))); + } + { + auto a = (0x12345678'90123456_u64).to_be_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8, 0x34_u8, 0x56_u8, 0x78_u8, 0x90_u8, + 0x12_u8, 0x34_u8, 0x56_u8))); + } +} + +TEST(u64, FromBeBytes) { + constexpr auto bytes = sus::Array(0x12_u8, 0x34_u8, 0x56_u8, 0x78_u8, + 0x90_u8, 0x12_u8, 0x34_u8, 0x56_u8); + if constexpr (std::endian::native == std::endian::little) { + EXPECT_EQ(u64::from_be_bytes(bytes), 0x12'34'56'78'90'12'34'56u); + } else { + EXPECT_EQ(u64::from_be_bytes(bytes), 0x56'34'12'90'78'56'34'12u); + } + static_assert(std::same_as); + + static_assert(std::endian::native != std::endian::little || + u64::from_be_bytes(bytes) == 0x12'34'56'78'90'12'34'56u); + static_assert(std::endian::native != std::endian::big || + u64::from_be_bytes(bytes) == 0x56'34'12'90'78'56'34'12u); +} + +TEST(u64, ToLeBytes) { + { + constexpr auto a = (0x12345678'90123456_u64).to_le_bytes(); + EXPECT_EQ(a, (sus::Array(0x56_u8, 0x34_u8, 0x12_u8, 0x90_u8, 0x78_u8, + 0x56_u8, 0x34_u8, 0x12_u8))); + } + { + auto a = (0x12345678'90123456_u64).to_le_bytes(); + EXPECT_EQ(a, (sus::Array(0x56_u8, 0x34_u8, 0x12_u8, 0x90_u8, 0x78_u8, + 0x56_u8, 0x34_u8, 0x12_u8))); + } +} + +TEST(u64, ToNeBytes) { + if constexpr (std::endian::native == std::endian::big) { + { + constexpr auto a = (0x12345678'90123456_u64).to_ne_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8, 0x34_u8, 0x56_u8, 0x78_u8, + 0x90_u8, 0x12_u8, 0x34_u8, 0x56_u8))); + } + { + auto a = (0x12345678'90123456_u64).to_ne_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8, 0x34_u8, 0x56_u8, 0x78_u8, + 0x90_u8, 0x12_u8, 0x34_u8, 0x56_u8))); + } + } else { + { + constexpr auto a = (0x12345678'90123456_u64).to_ne_bytes(); + EXPECT_EQ(a, (sus::Array(0x56_u8, 0x34_u8, 0x12_u8, 0x90_u8, + 0x78_u8, 0x56_u8, 0x34_u8, 0x12_u8))); + } + { + auto a = (0x12345678'90123456_u64).to_ne_bytes(); + EXPECT_EQ(a, (sus::Array(0x56_u8, 0x34_u8, 0x12_u8, 0x90_u8, + 0x78_u8, 0x56_u8, 0x34_u8, 0x12_u8))); + } + } +} + } // namespace diff --git a/sus/num/u8_unittest.cc b/sus/num/u8_unittest.cc index 94f8668cf..0b3d34045 100644 --- a/sus/num/u8_unittest.cc +++ b/sus/num/u8_unittest.cc @@ -15,14 +15,14 @@ #include #include "googletest/include/gtest/gtest.h" +#include "sus/cmp/eq.h" +#include "sus/cmp/ord.h" #include "sus/collections/array.h" #include "sus/construct/into.h" #include "sus/iter/__private/step.h" #include "sus/num/num_concepts.h" #include "sus/num/signed_integer.h" #include "sus/num/unsigned_integer.h" -#include "sus/cmp/eq.h" -#include "sus/cmp/ord.h" #include "sus/prelude.h" #include "sus/tuple/tuple.h" @@ -739,4 +739,135 @@ TEST(u8, fmt) { EXPECT_EQ(fmt::format("{:#x}", 123_u8), "0x7b"); } +TEST(u8, ToBe) { + if constexpr (std::endian::native == std::endian::little) { + constexpr auto a = (0x12_u8).to_be(); + EXPECT_EQ(a, 0x12_u8); + + EXPECT_EQ((0x12_u8).to_be(), 0x12_u8); + EXPECT_EQ((0_u8).to_be(), 0_u8); + EXPECT_EQ((1_u8 << 7_u32).to_be(), (1_u8 << 7_u32)); + } else { + constexpr auto a = (0x12_u8).to_be(); + EXPECT_EQ(a, 0x12_u8); + + EXPECT_EQ((0x12_u8).to_be(), 0x12_u8); + EXPECT_EQ((0_u8).to_be(), 0_u8); + EXPECT_EQ((1_u8 << 7_u32).to_be(), (1_u8 << 7_u32)); + } +} + +TEST(u8, FromBe) { + if constexpr (std::endian::native == std::endian::little) { + constexpr auto a = u8::from_be(0x12_u8); + EXPECT_EQ(a, 0x12_u8); + + EXPECT_EQ(u8::from_be(0x12_u8), 0x12_u8); + EXPECT_EQ(u8::from_be(0_u8), 0_u8); + EXPECT_EQ(u8::from_be(1_u8 << 7_u32), 1_u8 << 7_u32); + } else { + constexpr auto a = u8::from_be(0x12_u8); + EXPECT_EQ(a, 0x12_u8); + + EXPECT_EQ(u8::from_be(0x12_u8), 0x12_u8); + EXPECT_EQ(u8::from_be(0_u8), 0_u8); + EXPECT_EQ(u8::from_be(1_u8 << 7_u32), 1_u8 << 7_u32); + } +} + +TEST(u8, ToLe) { + if constexpr (std::endian::native == std::endian::big) { + constexpr auto a = (0x12_u8).to_le(); + EXPECT_EQ(a, 0x12_u8); + + EXPECT_EQ((0x12_u8).to_le(), 0x12_u8); + EXPECT_EQ((0_u8).to_le(), 0_u8); + EXPECT_EQ(u8::MIN.to_le(), 0x80_u8); + } else { + constexpr auto a = (0x12_u8).to_le(); + EXPECT_EQ(a, 0x12_u8); + + EXPECT_EQ((0x12_u8).to_le(), 0x12_u8); + EXPECT_EQ((0_u8).to_le(), 0_u8); + EXPECT_EQ(u8::MIN.to_le(), u8::MIN); + } +} + +TEST(u8, FromLe) { + if constexpr (std::endian::native == std::endian::big) { + constexpr auto a = u8::from_le(0x12_u8); + EXPECT_EQ(a, 0x12_u8); + + EXPECT_EQ(u8::from_le(0x12_u8), 0x12_u8); + EXPECT_EQ(u8::from_le(0_u8), 0_u8); + EXPECT_EQ(u8::from_le(u8::MIN), 0x80_u8); + } else { + constexpr auto a = u8::from_le(0x12_u8); + EXPECT_EQ(a, 0x12_u8); + + EXPECT_EQ(u8::from_le(0x12_u8), 0x12_u8); + EXPECT_EQ(u8::from_le(0_u8), 0_u8); + EXPECT_EQ(u8::from_le(u8::MIN), u8::MIN); + } +} + +TEST(u8, ToBeBytes) { + { + constexpr auto a = (0x12_u8).to_be_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8))); + } + { + auto a = (0x12_u8).to_be_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8))); + } +} + +TEST(u8, FromBeBytes) { + constexpr auto bytes = sus::Array(0x12_u8); + if constexpr (std::endian::native == std::endian::little) { + EXPECT_EQ(u8::from_be_bytes(bytes), 0x12u); + } else { + EXPECT_EQ(u8::from_be_bytes(bytes), 0x12u); + } + static_assert(std::same_as); + + static_assert(std::endian::native != std::endian::little || + u8::from_be_bytes(bytes) == 0x12u); + static_assert(std::endian::native != std::endian::big || + u8::from_be_bytes(bytes) == 0x12u); +} + +TEST(u8, ToLeBytes) { + { + constexpr auto a = (0x12_u8).to_le_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8))); + } + { + auto a = (0x12_u8).to_le_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8))); + } +} + +TEST(u8, ToNeBytes) { + if constexpr (std::endian::native == std::endian::big) { + { + constexpr auto a = (0x12_u8).to_ne_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8))); + } + { + auto a = (0x12_u8).to_ne_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8))); + } + } else { + { + constexpr auto a = (0x12_u8).to_ne_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8))); + } + { + auto a = (0x12_u8).to_ne_bytes(); + EXPECT_EQ(a, (sus::Array(0x12_u8))); + } + } +} + } // namespace