From: Yedidya Feldblum Date: Thu, 2 Mar 2017 05:20:44 +0000 (-0800) Subject: Refactor Endian X-Git-Tag: v2017.03.06.00~7 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=e473e0f9f9cc4fe10dc8920e8197c456e5c07140;p=folly.git Refactor Endian Summary: [Folly] Refactor `Endian`. Define `swap` for all types, instead of only declaring it for all types, but have its implementation call a function that only has a given set of non-template overloads for a given set of supported types. The effective change is that now `Endian::swap` will fail to compile, rather than compile but fail to link. And support floating-point types. Reviewed By: nbronson Differential Revision: D4615706 fbshipit-source-id: 5034e1e4466b8118a258d917ce8f4094460ca01a --- diff --git a/folly/Bits.h b/folly/Bits.h index cb4234c4..6c3c30ec 100644 --- a/folly/Bits.h +++ b/folly/Bits.h @@ -71,6 +71,7 @@ #include #include +#include #include #include #include @@ -232,43 +233,48 @@ inline typename std::enable_if< */ namespace detail { -template -struct EndianIntBase { - public: - static T swap(T x); -}; +template +struct uint_types_by_size; -#define FB_GEN(t, fn) \ - template <> \ - inline t EndianIntBase::swap(t x) { \ - return t(fn(std::make_unsigned::type(x))); \ - } +#define FB_GEN(sz, fn) \ + static inline uint##sz##_t byteswap_gen(uint##sz##_t v) { \ + return fn(v); \ + } \ + template <> \ + struct uint_types_by_size { \ + using type = uint##sz##_t; \ + }; -// fn(x) expands to (x) if the second argument is empty, which is exactly -// what we want for [u]int8_t. -FB_GEN( int8_t,) -FB_GEN(uint8_t,) +FB_GEN(8, uint8_t) #ifdef _MSC_VER -FB_GEN( int64_t, _byteswap_uint64) -FB_GEN(uint64_t, _byteswap_uint64) -FB_GEN( int32_t, _byteswap_ulong) -FB_GEN(uint32_t, _byteswap_ulong) -FB_GEN( int16_t, _byteswap_ushort) -FB_GEN(uint16_t, _byteswap_ushort) +FB_GEN(64, _byteswap_uint64) +FB_GEN(32, _byteswap_ulong) +FB_GEN(16, _byteswap_ushort) #else -FB_GEN( int64_t, __builtin_bswap64) -FB_GEN(uint64_t, __builtin_bswap64) -FB_GEN( int32_t, __builtin_bswap32) -FB_GEN(uint32_t, __builtin_bswap32) -FB_GEN( int16_t, __builtin_bswap16) -FB_GEN(uint16_t, __builtin_bswap16) +FB_GEN(64, __builtin_bswap64) +FB_GEN(32, __builtin_bswap32) +FB_GEN(16, __builtin_bswap16) #endif #undef FB_GEN template -struct EndianInt : public EndianIntBase { - public: +struct EndianInt { + static_assert( + (std::is_integral::value && !std::is_same::value) || + std::is_floating_point::value, + "template type parameter must be non-bool integral or floating point"); + static T swap(T x) { + // we implement this with memcpy because that is defined behavior in C++ + // we rely on compilers to optimize away the memcpy calls + constexpr auto s = sizeof(T); + using B = typename uint_types_by_size<8 * s>::type; + B b; + std::memcpy(&b, &x, s); + b = byteswap_gen(b); + std::memcpy(&x, &b, s); + return x; + } static T big(T x) { return kIsLittleEndian ? EndianInt::swap(x) : x; } diff --git a/folly/test/BitsTest.cpp b/folly/test/BitsTest.cpp index 2181ebef..af6945f6 100644 --- a/folly/test/BitsTest.cpp +++ b/folly/test/BitsTest.cpp @@ -18,6 +18,8 @@ #include +#include + #include using namespace folly; @@ -165,3 +167,21 @@ TEST(Bits, popcount) { EXPECT_EQ(32, popcount(uint32_t(-1))); EXPECT_EQ(64, popcount(uint64_t(-1))); } + +TEST(Bits, Endian_swap_uint) { + EXPECT_EQ(uint8_t(0xda), Endian::swap(uint8_t(0xda))); + EXPECT_EQ(uint16_t(0x4175), Endian::swap(uint16_t(0x7541))); + EXPECT_EQ(uint32_t(0x42efb918), Endian::swap(uint32_t(0x18b9ef42))); + EXPECT_EQ( + uint64_t(0xa244f5e862c71d8a), Endian::swap(uint64_t(0x8a1dc762e8f544a2))); +} + +TEST(Bits, Endian_swap_real) { + std::mt19937_64 rng; + auto f = std::uniform_real_distribution()(rng); + EXPECT_NE(f, Endian::swap(f)); + EXPECT_EQ(f, Endian::swap(Endian::swap(f))); + auto d = std::uniform_real_distribution()(rng); + EXPECT_NE(d, Endian::swap(d)); + EXPECT_EQ(d, Endian::swap(Endian::swap(d))); +}