Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add big-endian support #257

Merged
merged 2 commits into from
Mar 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ commands:
sudo apt -q update
sudo apt install -qy libgmp-dev

install_powerpc64:
steps:
- run:
name: "Install powerpc64 toolchain"
command: |
sudo apt -q update
sudo apt -qy install g++-powerpc64-linux-gnu qemu-user-static

check_code_format:
steps:
- run:
Expand Down Expand Up @@ -195,6 +203,15 @@ jobs:
- build_and_test
- benchmark

powerpc64:
environment:
BUILD_TYPE: Release
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=~/project/cmake/toolchains/powerpc64.cmake -DINTX_BENCHMARKING=OFF
executor: linux-gcc-latest
steps:
- install_powerpc64
- build_and_test

arm64:
environment:
BUILD_TYPE: Release
Expand Down Expand Up @@ -299,3 +316,4 @@ workflows:
- macos
- cmake-min
- arm64
- powerpc64
15 changes: 15 additions & 0 deletions cmake/toolchains/powerpc64.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Cable: CMake Bootstrap Library.
# Copyright 2018 Pawel Bylica.
# Licensed under the Apache License, Version 2.0. See the LICENSE file.

set(CMAKE_SYSTEM_PROCESSOR powerpc64)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_C_COMPILER powerpc64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER powerpc64-linux-gnu-g++)

set(CMAKE_FIND_ROOT_PATH /usr/powerpc64-linux-gnu)
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

set(CMAKE_CROSSCOMPILING_EMULATOR qemu-ppc64-static;-L;${CMAKE_FIND_ROOT_PATH})
63 changes: 55 additions & 8 deletions include/intx/intx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ using builtin_uint128 = unsigned __int128;
#pragma GCC diagnostic pop
#endif

constexpr bool byte_order_is_little_endian =
#if defined(__BYTE_ORDER__)
(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
#elif defined(_WIN32)
true; // On Windows assume little endian.
#else
#error "Unknown endianness"
#endif

template <unsigned N>
struct uint;

Expand Down Expand Up @@ -2016,6 +2025,43 @@ inline constexpr uint512 operator"" _u512(const char* s)
return from_string<uint512>(s);
}


/// Convert native representation to/from little-endian byte order.
/// intx and built-in integral types are supported.
template <typename T>
inline constexpr T to_little_endian(const T& x) noexcept
{
if constexpr (byte_order_is_little_endian)
return x;
else if constexpr (std::is_integral_v<T>)
return bswap(x);
else // Wordwise bswap.
{
T r;
for (size_t i = 0; i < T::num_words; ++i)
r[i] = bswap(x[i]);
return r;
}
}

/// Convert native representation to/from big-endian byte order.
/// intx and built-in integral types are supported.
template <typename T>
inline constexpr T to_big_endian(const T& x) noexcept
{
if constexpr (byte_order_is_little_endian)
return bswap(x);
else if constexpr (std::is_integral_v<T>)
return x;
else // Swap words.
{
T r;
for (size_t i = 0; i < T::num_words; ++i)
r[T::num_words - 1 - i] = x[i];
return r;
}
}

namespace le // Conversions to/from LE bytes.
{
template <typename IntT, unsigned M>
Expand All @@ -2025,13 +2071,15 @@ inline IntT load(const uint8_t (&src)[M]) noexcept
"the size of source bytes must match the size of the destination uint");
IntT x;
std::memcpy(&x, src, sizeof(x));
x = to_little_endian(x);
return x;
}

template <unsigned N>
inline void store(uint8_t (&dst)[N / 8], const uint<N>& x) noexcept
{
std::memcpy(dst, &x, sizeof(x));
const auto d = to_little_endian(x);
std::memcpy(dst, &d, sizeof(d));
}

} // namespace le
Expand All @@ -2048,7 +2096,7 @@ inline IntT load(const uint8_t (&src)[M]) noexcept
"the size of source bytes must not exceed the size of the destination uint");
IntT x;
std::memcpy(&as_bytes(x)[IntT::num_bits / 8 - M], src, M);
x = bswap(x);
x = to_big_endian(x);
return x;
}

Expand All @@ -2062,7 +2110,7 @@ inline IntT load(const T& t) noexcept
template <unsigned N>
inline void store(uint8_t (&dst)[N / 8], const uint<N>& x) noexcept
{
const auto d = bswap(x);
const auto d = to_big_endian(x);
std::memcpy(dst, &d, sizeof(d));
}

Expand All @@ -2083,9 +2131,8 @@ template <unsigned M, unsigned N>
inline void trunc(uint8_t (&dst)[M], const uint<N>& x) noexcept
{
static_assert(M < N / 8, "destination must be smaller than the source value");
const auto d = bswap(x);
const auto b = as_bytes(d);
std::memcpy(dst, &b[sizeof(d) - M], M);
const auto d = to_big_endian(x);
std::memcpy(dst, &as_bytes(d)[sizeof(d) - M], M);
}

/// Stores the truncated value of an uint in the .bytes field of an object of type T.
Expand All @@ -2106,7 +2153,7 @@ inline IntT load(const uint8_t* src) noexcept
{
IntT x;
std::memcpy(&x, src, sizeof(x));
x = bswap(x);
x = to_big_endian(x);
return x;
}

Expand All @@ -2115,7 +2162,7 @@ inline IntT load(const uint8_t* src) noexcept
template <unsigned N>
inline void store(uint8_t* dst, const uint<N>& x) noexcept
{
const auto d = bswap(x);
const auto d = to_big_endian(x);
std::memcpy(dst, &d, sizeof(d));
}
} // namespace unsafe
Expand Down
12 changes: 12 additions & 0 deletions test/unittests/test_builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ static_assert(clz_generic(uint64_t{1}) == 63);
static_assert(clz_generic(uint64_t{3}) == 62);
static_assert(clz_generic(uint64_t{9}) == 60);

static constexpr auto is_le = byte_order_is_little_endian;
static_assert(to_little_endian(uint8_t{0x0a}) == 0x0a);
static_assert(to_little_endian(uint16_t{0x0b0a}) == (is_le ? 0x0b0a : 0x0a0b));
static_assert(to_little_endian(uint32_t{0x0d0c0b0a}) == (is_le ? 0x0d0c0b0a : 0x0a0b0c0d));
static_assert(to_little_endian(uint64_t{0x02010f0e0d0c0b0a}) ==
(is_le ? 0x02010f0e0d0c0b0a : 0x0a0b0c0d0e0f0102));
static_assert(to_big_endian(uint8_t{0x0a}) == 0x0a);
static_assert(to_big_endian(uint16_t{0x0b0a}) == (is_le ? 0x0a0b : 0x0b0a));
static_assert(to_big_endian(uint32_t{0x0d0c0b0a}) == (is_le ? 0x0a0b0c0d : 0x0d0c0b0a));
static_assert(to_big_endian(uint64_t{0x02010f0e0d0c0b0a}) ==
(is_le ? 0x0a0b0c0d0e0f0102 : 0x02010f0e0d0c0b0a));


TEST(builtins, clz64_single_one)
{
Expand Down
3 changes: 2 additions & 1 deletion test/unittests/test_intx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ TYPED_TEST(uint_test, to_string_base)

TYPED_TEST(uint_test, as_bytes)
{
constexpr auto x = TypeParam{0xa05};
constexpr auto x = to_little_endian(TypeParam{0xa05});
const auto b = as_bytes(x);
EXPECT_EQ(b[0], 5);
EXPECT_EQ(b[1], 0xa);
Expand All @@ -281,5 +281,6 @@ TYPED_TEST(uint_test, as_bytes)
auto d = as_bytes(y);
d[0] = 3;
d[1] = 0xc;
y = to_little_endian(y);
EXPECT_EQ(y, 0xc03);
}