diff --git a/absl/base/config.h b/absl/base/config.h index 6015676fe29..7514b86e5e7 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -466,6 +466,9 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // // Checks the endianness of the platform. // +// Prefer using `std::endian` in C++20, or `absl::endian` from +// absl/numeric/bits.h prior to C++20. +// // Notes: uses the built in endian macros provided by GCC (since 4.6) and // Clang (since 3.2); see // https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html. diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h index 943f3d97e79..e1a67f587b0 100644 --- a/absl/base/internal/endian.h +++ b/absl/base/internal/endian.h @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. // +// This file is for Abseil internal use only. +// See //absl/numeric/bits.h for supported functions related to endian-ness. #ifndef ABSL_BASE_INTERNAL_ENDIAN_H_ #define ABSL_BASE_INTERNAL_ENDIAN_H_ @@ -28,44 +30,38 @@ namespace absl { ABSL_NAMESPACE_BEGIN -inline uint64_t gbswap_64(uint64_t host_int) { +constexpr uint64_t gbswap_64(uint64_t x) { #if ABSL_HAVE_BUILTIN(__builtin_bswap64) || defined(__GNUC__) - return __builtin_bswap64(host_int); -#elif defined(_MSC_VER) - return _byteswap_uint64(host_int); + return __builtin_bswap64(x); #else - return (((host_int & uint64_t{0xFF}) << 56) | - ((host_int & uint64_t{0xFF00}) << 40) | - ((host_int & uint64_t{0xFF0000}) << 24) | - ((host_int & uint64_t{0xFF000000}) << 8) | - ((host_int & uint64_t{0xFF00000000}) >> 8) | - ((host_int & uint64_t{0xFF0000000000}) >> 24) | - ((host_int & uint64_t{0xFF000000000000}) >> 40) | - ((host_int & uint64_t{0xFF00000000000000}) >> 56)); + return (((x & uint64_t{0xFF}) << 56) | + ((x & uint64_t{0xFF00}) << 40) | + ((x & uint64_t{0xFF0000}) << 24) | + ((x & uint64_t{0xFF000000}) << 8) | + ((x & uint64_t{0xFF00000000}) >> 8) | + ((x & uint64_t{0xFF0000000000}) >> 24) | + ((x & uint64_t{0xFF000000000000}) >> 40) | + ((x & uint64_t{0xFF00000000000000}) >> 56)); #endif } -inline uint32_t gbswap_32(uint32_t host_int) { +constexpr uint32_t gbswap_32(uint32_t x) { #if ABSL_HAVE_BUILTIN(__builtin_bswap32) || defined(__GNUC__) - return __builtin_bswap32(host_int); -#elif defined(_MSC_VER) - return _byteswap_ulong(host_int); + return __builtin_bswap32(x); #else - return (((host_int & uint32_t{0xFF}) << 24) | - ((host_int & uint32_t{0xFF00}) << 8) | - ((host_int & uint32_t{0xFF0000}) >> 8) | - ((host_int & uint32_t{0xFF000000}) >> 24)); + return (((x & uint32_t{0xFF}) << 24) | + ((x & uint32_t{0xFF00}) << 8) | + ((x & uint32_t{0xFF0000}) >> 8) | + ((x & uint32_t{0xFF000000}) >> 24)); #endif } -inline uint16_t gbswap_16(uint16_t host_int) { +constexpr uint16_t gbswap_16(uint16_t x) { #if ABSL_HAVE_BUILTIN(__builtin_bswap16) || defined(__GNUC__) - return __builtin_bswap16(host_int); -#elif defined(_MSC_VER) - return _byteswap_ushort(host_int); + return __builtin_bswap16(x); #else - return (((host_int & uint16_t{0xFF}) << 8) | - ((host_int & uint16_t{0xFF00}) >> 8)); + return (((x & uint16_t{0xFF}) << 8) | + ((x & uint16_t{0xFF00}) >> 8)); #endif } diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel index 7b92eed5830..4503a157721 100644 --- a/absl/numeric/BUILD.bazel +++ b/absl/numeric/BUILD.bazel @@ -41,6 +41,7 @@ cc_library( deps = [ "//absl/base:config", "//absl/base:core_headers", + "//absl/base:endian", ], ) diff --git a/absl/numeric/CMakeLists.txt b/absl/numeric/CMakeLists.txt index da3b6efe618..68446b012b5 100644 --- a/absl/numeric/CMakeLists.txt +++ b/absl/numeric/CMakeLists.txt @@ -23,7 +23,9 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::config absl::core_headers + absl::endian PUBLIC ) diff --git a/absl/numeric/bits.h b/absl/numeric/bits.h index 2871ca8399f..9a0c2290bc8 100644 --- a/absl/numeric/bits.h +++ b/absl/numeric/bits.h @@ -27,6 +27,10 @@ // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1355r2.html // P1956R1: // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1956r1.pdf +// P0463R1 +// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0463r1.html +// P1272R4 +// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1272r4.html // // When using a standard library that implements these functions, we use the // standard library's implementation. @@ -45,6 +49,7 @@ #endif #include "absl/base/attributes.h" +#include "absl/base/internal/endian.h" #include "absl/numeric/internal/bits.h" namespace absl { @@ -190,6 +195,67 @@ ABSL_INTERNAL_CONSTEXPR_CLZ inline #endif +#if defined(__cpp_lib_endian) && __cpp_lib_endian >= 201907L + +// https://en.cppreference.com/w/cpp/types/endian +// +// Indicates the endianness of all scalar types: +// * If all scalar types are little-endian, `absl::endian::native` equals +// absl::endian::little. +// * If all scalar types are big-endian, `absl::endian::native` equals +// `absl::endian::big`. +// * Platforms that use anything else are unsupported. +using std::endian; + +#else + +enum class endian { + little, + big, +#if defined(ABSL_IS_LITTLE_ENDIAN) + native = little +#elif defined(ABSL_IS_BIG_ENDIAN) + native = big +#else +#error "Endian detection needs to be set up for this platform" +#endif +}; + +#endif // defined(__cpp_lib_endian) && __cpp_lib_endian >= 201907L + +#if defined(__cpp_lib_byteswap) && __cpp_lib_byteswap >= 202110L + +// https://en.cppreference.com/w/cpp/numeric/byteswap +// +// Reverses the bytes in the given integer value `x`. +// +// `absl::byteswap` participates in overload resolution only if `T` satisfies +// integral, i.e., `T` is an integer type. The program is ill-formed if `T` has +// padding bits. +using std::byteswap; + +#else + +template +[[nodiscard]] constexpr T byteswap(T x) noexcept { + static_assert(std::is_integral_v, + "byteswap requires an integral argument"); + static_assert( + sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, + "byteswap works only with 8, 16, 32, or 64-bit integers"); + if constexpr (sizeof(T) == 1) { + return x; + } else if constexpr (sizeof(T) == 2) { + return static_cast(gbswap_16(static_cast(x))); + } else if constexpr (sizeof(T) == 4) { + return static_cast(gbswap_32(static_cast(x))); + } else if constexpr (sizeof(T) == 8) { + return static_cast(gbswap_64(static_cast(x))); + } +} + +#endif // defined(__cpp_lib_byteswap) && __cpp_lib_byteswap >= 202110L + ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/numeric/bits_test.cc b/absl/numeric/bits_test.cc index 14955eb3519..3b71cccf88a 100644 --- a/absl/numeric/bits_test.cc +++ b/absl/numeric/bits_test.cc @@ -14,6 +14,7 @@ #include "absl/numeric/bits.h" +#include #include #include @@ -636,6 +637,61 @@ static_assert(ABSL_INTERNAL_HAS_CONSTEXPR_CLZ, "clz should be constexpr"); static_assert(ABSL_INTERNAL_HAS_CONSTEXPR_CTZ, "ctz should be constexpr"); #endif +TEST(Endian, Comparison) { +#if defined(ABSL_IS_LITTLE_ENDIAN) + static_assert(absl::endian::native == absl::endian::little); + static_assert(absl::endian::native != absl::endian::big); +#endif +#if defined(ABSL_IS_BIG_ENDIAN) + static_assert(absl::endian::native != absl::endian::little); + static_assert(absl::endian::native == absl::endian::big); +#endif +} + +TEST(Byteswap, Constexpr) { + static_assert(absl::byteswap(0x12) == 0x12); + static_assert(absl::byteswap(0x1234) == 0x3412); + static_assert(absl::byteswap(0x12345678) == 0x78563412); + static_assert(absl::byteswap(0x123456789abcdef0) == + static_cast(0xf0debc9a78563412)); + static_assert(absl::byteswap(0x21) == 0x21); + static_assert(absl::byteswap(0x4321) == 0x2143); + static_assert(absl::byteswap(0x87654321) == 0x21436587); + static_assert(absl::byteswap(0xfedcba9876543210) == + static_cast(0x1032547698badcfe)); + static_assert(absl::byteswap(static_cast(0xdeadbeef)) == + static_cast(0xefbeadde)); +} + +TEST(Byteswap, NotConstexpr) { + int8_t a = 0x12; + int16_t b = 0x1234; + int32_t c = 0x12345678; + int64_t d = 0x123456789abcdef0; + uint8_t e = 0x21; + uint16_t f = 0x4321; + uint32_t g = 0x87654321; + uint64_t h = 0xfedcba9876543210; + EXPECT_EQ(absl::byteswap(a), 0x12); + EXPECT_EQ(absl::byteswap(b), 0x3412); + EXPECT_EQ(absl::byteswap(c), 0x78563412); + EXPECT_EQ(absl::byteswap(d), 0xf0debc9a78563412); + EXPECT_EQ(absl::byteswap(e), 0x21); + EXPECT_EQ(absl::byteswap(f), 0x2143); + EXPECT_EQ(absl::byteswap(g), 0x21436587); + EXPECT_EQ(absl::byteswap(h), 0x1032547698badcfe); + EXPECT_EQ(absl::byteswap(absl::byteswap(a)), a); + EXPECT_EQ(absl::byteswap(absl::byteswap(b)), b); + EXPECT_EQ(absl::byteswap(absl::byteswap(c)), c); + EXPECT_EQ(absl::byteswap(absl::byteswap(d)), d); + EXPECT_EQ(absl::byteswap(absl::byteswap(e)), e); + EXPECT_EQ(absl::byteswap(absl::byteswap(f)), f); + EXPECT_EQ(absl::byteswap(absl::byteswap(g)), g); + EXPECT_EQ(absl::byteswap(absl::byteswap(h)), h); + EXPECT_EQ(absl::byteswap(0xdeadbeef), 0xefbeadde); + EXPECT_EQ(absl::byteswap(0xdeadbeef), 0xefbeadde); +} + } // namespace ABSL_NAMESPACE_END } // namespace absl