From 64ee02d7bd90229a3878ac0f7135c1c5acecfc76 Mon Sep 17 00:00:00 2001 From: yharby Date: Sun, 14 Dec 2025 11:14:48 +0200 Subject: [PATCH] fix: Add fallback byteswap/endian implementations for older abseil versions s2geometry v0.13.1 uses absl::byteswap() and absl::endian which were only added in abseil LTS 2025.01.27. This causes compilation failures when building with older abseil versions. This commit adds: - Fallback byteswap implementations using compiler intrinsics (__builtin_bswap16/32/64 for GCC/Clang) with manual fallbacks - Compile-time endianness detection via __BYTE_ORDER__ macros - Maintains backwards compatibility while still working with newer abseil Fixes #489 --- src/s2/util/endian/endian.h | 88 ++++++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/src/s2/util/endian/endian.h b/src/s2/util/endian/endian.h index 61ace1c3f..148474e8c 100644 --- a/src/s2/util/endian/endian.h +++ b/src/s2/util/endian/endian.h @@ -18,18 +18,83 @@ #include -#include "absl/numeric/bits.h" #include "absl/numeric/int128.h" #include "s2/util/gtl/unaligned.h" +// Check for absl::byteswap availability (added in abseil LTS 2025.01.27) +#if __has_include("absl/numeric/bits.h") +#include "absl/numeric/bits.h" +#endif + +// Provide fallback byteswap implementations for older abseil versions namespace s2endian { + +// Fallback byteswap for uint16_t +inline uint16_t byteswap_u16(uint16_t x) { +#if defined(__GNUC__) || defined(__clang__) + return __builtin_bswap16(x); +#else + return static_cast((x >> 8) | (x << 8)); +#endif +} + +// Fallback byteswap for uint32_t +inline uint32_t byteswap_u32(uint32_t x) { +#if defined(__GNUC__) || defined(__clang__) + return __builtin_bswap32(x); +#else + return ((x & 0xFF000000U) >> 24) | + ((x & 0x00FF0000U) >> 8) | + ((x & 0x0000FF00U) << 8) | + ((x & 0x000000FFU) << 24); +#endif +} + +// Fallback byteswap for uint64_t +inline uint64_t byteswap_u64(uint64_t x) { +#if defined(__GNUC__) || defined(__clang__) + return __builtin_bswap64(x); +#else + return ((x & 0xFF00000000000000ULL) >> 56) | + ((x & 0x00FF000000000000ULL) >> 40) | + ((x & 0x0000FF0000000000ULL) >> 24) | + ((x & 0x000000FF00000000ULL) >> 8) | + ((x & 0x00000000FF000000ULL) << 8) | + ((x & 0x0000000000FF0000ULL) << 24) | + ((x & 0x000000000000FF00ULL) << 40) | + ((x & 0x00000000000000FFULL) << 56); +#endif +} + // TODO(user): Trim unused Encoder functions and drop int128 API. inline absl::uint128 byteswap(absl::uint128 host_int) { - return absl::MakeUint128(absl::byteswap(absl::Uint128Low64(host_int)), - absl::byteswap(absl::Uint128High64(host_int))); + return absl::MakeUint128(byteswap_u64(absl::Uint128Low64(host_int)), + byteswap_u64(absl::Uint128High64(host_int))); } + } // namespace s2endian +// Detect endianness at compile time +namespace s2endian_detail { +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + defined(__ORDER_BIG_ENDIAN__) +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +constexpr bool kIsLittleEndian = true; +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +constexpr bool kIsLittleEndian = false; +#else +#error "Unknown endianness" +#endif +#elif defined(_WIN32) || defined(__x86_64__) || defined(__i386__) || \ + defined(__aarch64__) || defined(__arm__) +// Most common platforms are little-endian +constexpr bool kIsLittleEndian = true; +#else +// Fallback: assume little-endian (safe for most platforms) +constexpr bool kIsLittleEndian = true; +#endif +} // namespace s2endian_detail + // Utilities to convert numbers between the current hosts's native byte // order and little-endian byte order // @@ -54,7 +119,7 @@ class LittleEndian { } static constexpr bool IsLittleEndian() { - return absl::endian::native == absl::endian::little; + return s2endian_detail::kIsLittleEndian; } // Functions to do unaligned loads and stores in little-endian order. @@ -109,15 +174,16 @@ class LittleEndian { } private: + // Byteswap implementations for each type + static uint16_t do_byteswap(uint16_t x) { return s2endian::byteswap_u16(x); } + static uint32_t do_byteswap(uint32_t x) { return s2endian::byteswap_u32(x); } + static uint64_t do_byteswap(uint64_t x) { return s2endian::byteswap_u64(x); } + static absl::uint128 do_byteswap(absl::uint128 x) { return s2endian::byteswap(x); } + template static T byteswap_if_big_endian(T x) { - static_assert(absl::endian::native == absl::endian::big || - absl::endian::native == absl::endian::little, - "Unsupported endianness."); - if constexpr (absl::endian::native == absl::endian::big) { - using absl::byteswap; - using s2endian::byteswap; - x = byteswap(x); + if constexpr (!s2endian_detail::kIsLittleEndian) { + return do_byteswap(x); } return x; }