mirror of
https://github.com/abseil/abseil-cpp.git
synced 2026-06-04 12:07:05 +08:00
bits.h: Add absl::endian and absl::byteswap polyfills
I had to remove the MSVC implementation since it is not constexpr. https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/byteswap-uint64-byteswap-ulong-byteswap-ushort?view=msvc-170 But all major compilers understand the portable implementation is just a bswap: https://godbolt.org/z/oMnW3xsns Closes #1198 PiperOrigin-RevId: 734626285 Change-Id: Ibca79f408f225c894f3c2b95f7086f891627db9f
This commit is contained in:
committed by
Copybara-Service
parent
70ba73e6b1
commit
25bce1236b
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ cc_library(
|
||||
deps = [
|
||||
"//absl/base:config",
|
||||
"//absl/base:core_headers",
|
||||
"//absl/base:endian",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -23,7 +23,9 @@ absl_cc_library(
|
||||
COPTS
|
||||
${ABSL_DEFAULT_COPTS}
|
||||
DEPS
|
||||
absl::config
|
||||
absl::core_headers
|
||||
absl::endian
|
||||
PUBLIC
|
||||
)
|
||||
|
||||
|
||||
@@ -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 <class T>
|
||||
[[nodiscard]] constexpr T byteswap(T x) noexcept {
|
||||
static_assert(std::is_integral_v<T>,
|
||||
"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<T>(gbswap_16(static_cast<uint16_t>(x)));
|
||||
} else if constexpr (sizeof(T) == 4) {
|
||||
return static_cast<T>(gbswap_32(static_cast<uint32_t>(x)));
|
||||
} else if constexpr (sizeof(T) == 8) {
|
||||
return static_cast<T>(gbswap_64(static_cast<uint64_t>(x)));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // defined(__cpp_lib_byteswap) && __cpp_lib_byteswap >= 202110L
|
||||
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "absl/numeric/bits.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
@@ -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<int8_t>(0x12) == 0x12);
|
||||
static_assert(absl::byteswap<int16_t>(0x1234) == 0x3412);
|
||||
static_assert(absl::byteswap<int32_t>(0x12345678) == 0x78563412);
|
||||
static_assert(absl::byteswap<int64_t>(0x123456789abcdef0) ==
|
||||
static_cast<int64_t>(0xf0debc9a78563412));
|
||||
static_assert(absl::byteswap<uint8_t>(0x21) == 0x21);
|
||||
static_assert(absl::byteswap<uint16_t>(0x4321) == 0x2143);
|
||||
static_assert(absl::byteswap<uint32_t>(0x87654321) == 0x21436587);
|
||||
static_assert(absl::byteswap<uint64_t>(0xfedcba9876543210) ==
|
||||
static_cast<uint64_t>(0x1032547698badcfe));
|
||||
static_assert(absl::byteswap<int32_t>(static_cast<int32_t>(0xdeadbeef)) ==
|
||||
static_cast<int32_t>(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<int8_t>(a), 0x12);
|
||||
EXPECT_EQ(absl::byteswap<int16_t>(b), 0x3412);
|
||||
EXPECT_EQ(absl::byteswap(c), 0x78563412);
|
||||
EXPECT_EQ(absl::byteswap(d), 0xf0debc9a78563412);
|
||||
EXPECT_EQ(absl::byteswap<uint8_t>(e), 0x21);
|
||||
EXPECT_EQ(absl::byteswap<uint16_t>(f), 0x2143);
|
||||
EXPECT_EQ(absl::byteswap(g), 0x21436587);
|
||||
EXPECT_EQ(absl::byteswap(h), 0x1032547698badcfe);
|
||||
EXPECT_EQ(absl::byteswap(absl::byteswap<int8_t>(a)), a);
|
||||
EXPECT_EQ(absl::byteswap(absl::byteswap<int16_t>(b)), b);
|
||||
EXPECT_EQ(absl::byteswap(absl::byteswap(c)), c);
|
||||
EXPECT_EQ(absl::byteswap(absl::byteswap(d)), d);
|
||||
EXPECT_EQ(absl::byteswap(absl::byteswap<uint8_t>(e)), e);
|
||||
EXPECT_EQ(absl::byteswap(absl::byteswap<uint16_t>(f)), f);
|
||||
EXPECT_EQ(absl::byteswap(absl::byteswap(g)), g);
|
||||
EXPECT_EQ(absl::byteswap(absl::byteswap(h)), h);
|
||||
EXPECT_EQ(absl::byteswap<uint32_t>(0xdeadbeef), 0xefbeadde);
|
||||
EXPECT_EQ(absl::byteswap<const uint32_t>(0xdeadbeef), 0xefbeadde);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
Reference in New Issue
Block a user