diff --git a/src/estd/bit.h b/src/estd/bit.h index 45d7219e..4c5941ac 100644 --- a/src/estd/bit.h +++ b/src/estd/bit.h @@ -1,6 +1,7 @@ #pragma once #include "cstdint.h" +#include "internal/feature/bit.h" namespace estd { @@ -20,7 +21,8 @@ enum class endian } -#if __cpp_lib_byteswap >= 202110L +#if (FEATURE_ESTD_BYTESWAP_MODE == FEATURE_ESTD_BYTESWAP_AUTO && __cpp_lib_byteswap >= 202110L) || \ + FEATURE_ESTD_BYTESWAP_MODE == FEATURE_ESTD_BYTESWAP_STD #include namespace estd { @@ -32,38 +34,70 @@ using std::byteswap; #else namespace estd { -template -constexpr Unsigned byteswap(Unsigned n) noexcept; +namespace detail { +#if FEATURE_ESTD_BYTESWAP_MODE == FEATURE_ESTD_BYTESWAP_XOR -#if __has_builtin(__builtin_bswap16) -template <> -constexpr uint16_t byteswap(uint16_t n) noexcept +#error manual xor non gcc/ non std mode not yet supported + +#else // not xor (gcc) + +#if __has_builtin(__builtin_bswap16) || FEATURE_ESTD_BYTESWAP_MODE == FEATURE_ESTD_BYTESWAP_GCC + +constexpr uint16_t +byteswap(uint16_t n) noexcept +{ + return __builtin_bswap16(n); +} + +// ISO/IEC 9899:201x aka WG14 N1570 (https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) +// Section 6.2.6.2 indicates one signed bit is present, and section 3.4.1 immplies it's always +// the high order bit. This gives us confidence that we can swap bits around safely, trusting that +// whoever "unswaps" them will end up with the expected high order signed bit. + +constexpr int16_t +byteswap(int16_t n) noexcept { return __builtin_bswap16(n); } + #endif -#if __has_builtin(__builtin_bswap32) -template <> -constexpr uint32_t byteswap(uint32_t n) noexcept +#if __has_builtin(__builtin_bswap32) || FEATURE_ESTD_BYTESWAP_MODE == FEATURE_ESTD_BYTESWAP_GCC + +constexpr uint32_t +byteswap(uint32_t n) noexcept { return __builtin_bswap32(n); } + +constexpr int32_t +byteswap(int32_t n) noexcept +{ + return __builtin_bswap32(n); +} + #endif -#if __has_builtin(__builtin_bswap64) +#if __has_builtin(__builtin_bswap64) || FEATURE_ESTD_BYTESWAP_MODE == FEATURE_ESTD_BYTESWAP_GCC -template <> -constexpr uint64_t byteswap(uint64_t n) noexcept +constexpr uint64_t +byteswap(uint64_t n) noexcept { -#if __has_builtin(__builtin_bswap32) return __builtin_bswap64(n); -#else - return 0; -#endif } + #endif +#endif // not xor (gcc) } +// Since spec indicates a templated function, we wrap up the specific ones +// in detail. This is better than specializing, since that can lead to linker ambiguities +template +constexpr T byteswap(T n) noexcept { return detail::byteswap(n); } + +} + + + #endif \ No newline at end of file diff --git a/src/estd/internal/feature/bit.h b/src/estd/internal/feature/bit.h new file mode 100644 index 00000000..3dca61e8 --- /dev/null +++ b/src/estd/internal/feature/bit.h @@ -0,0 +1,10 @@ +#pragma once + +#define FEATURE_ESTD_BYTESWAP_AUTO 0 // Prefer STD, then GCC (if available), then XOR +#define FEATURE_ESTD_BYTESWAP_STD 1 // Force STD mode, ignoring compiler indication of availability +#define FEATURE_ESTD_BYTESWAP_GCC 2 // Force GCC mode, ignoring compiler indication of availability +#define FEATURE_ESTD_BYTESWAP_XOR 3 // Force XOR (manual) mode, not implemented at this time + +#ifndef FEATURE_ESTD_BYTESWAP_MODE +#define FEATURE_ESTD_BYTESWAP_MODE FEATURE_ESTD_BYTESWAP_AUTO +#endif diff --git a/test/catch/bit-test.cpp b/test/catch/bit-test.cpp index c6cc8255..6bc3d8f4 100644 --- a/test/catch/bit-test.cpp +++ b/test/catch/bit-test.cpp @@ -6,8 +6,17 @@ TEST_CASE("bit operations") { SECTION("byteswap") { - auto v = estd::byteswap(0x1234); + SECTION("uint16_t") + { + auto v = estd::byteswap(0x1234); - REQUIRE(v == 0x3412); + REQUIRE(v == 0x3412); + } + SECTION("int32_t") + { + auto v = estd::byteswap(0x1234); + + REQUIRE(v == 0x34120000); + } } } \ No newline at end of file