diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dd60d49..1832f8f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -495,6 +495,7 @@ if (TESTS) crypto/tea crypto/xtea crypto/xxtea + endian exe extent fixed diff --git a/endian.hpp b/endian.hpp index aef5087e..e63d7ff9 100644 --- a/endian.hpp +++ b/endian.hpp @@ -33,7 +33,7 @@ namespace util { //------------------------------------------------------------------------- template constexpr T - bswap (T); + bswap (T) noexcept; // CXX: Update using "if constexpr" when available. It doesn't seem to be // worth the dicking about to use enable_if_t to differentiate the @@ -42,7 +42,7 @@ namespace util { template <> \ constexpr \ int##S##_t \ - bswap (int##S##_t v) { \ + bswap (int##S##_t v) noexcept { \ const union { \ int##S##_t s; \ uint##S##_t u; \ @@ -55,12 +55,31 @@ namespace util { SIGNED_BSWAP(32) SIGNED_BSWAP(64) - template <> constexpr int8_t bswap ( int8_t v) { return v; } - template <> constexpr uint8_t bswap (uint8_t v) { return v; } + template <> constexpr int8_t bswap ( int8_t v) noexcept { return v; } + template <> constexpr uint8_t bswap (uint8_t v) noexcept { return v; } - template <> constexpr uint16_t bswap (uint16_t v) { return __builtin_bswap16 (v); } - template <> constexpr uint32_t bswap (uint32_t v) { return __builtin_bswap32 (v); } - template <> constexpr uint64_t bswap (uint64_t v) { return __builtin_bswap64 (v); } + template <> constexpr uint16_t bswap (uint16_t v) noexcept { return __builtin_bswap16 (v); } + template <> constexpr uint32_t bswap (uint32_t v) noexcept { return __builtin_bswap32 (v); } + template <> constexpr uint64_t bswap (uint64_t v) noexcept { return __builtin_bswap64 (v); } + + + // use a type-punning union to punt the byte swapping operation to the + // unsigned integer code. + // + // this is debatably safe under production compilers like gcc + clang + // despite what the standard may say about unions. + template <> + constexpr float + bswap (float v) noexcept + { + union { + float f; + uint32_t u; + } temp { .f = v }; + + temp.u = bswap (temp.u); + return temp.f; + } //------------------------------------------------------------------------- diff --git a/test/endian.cpp b/test/endian.cpp new file mode 100644 index 00000000..d91f4ad2 --- /dev/null +++ b/test/endian.cpp @@ -0,0 +1,31 @@ +#include "tap.hpp" + +#include "endian.hpp" + + +/////////////////////////////////////////////////////////////////////////////// +int +main (void) +{ + util::TAP::logger tap; + + { + uint32_t a = 0x12345678, b = 0x78563412; + tap.expect_eq (a, util::bswap (b), "u32 byteswap"); + } + + { + // try to byte swap the pattern for 1.0f + // + // it may not be the most robust test, but it'll catch the most + // egregious errors for the time being. + union { + uint32_t u; + float f; + } data { .u = 0x0000803f /* 0x3f800000 == 1.f */ }; + + tap.expect_eq (util::bswap (data.f), 1.f, "f32 byteswap"); + }; + + return tap.status (); +} \ No newline at end of file