Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 77 additions & 11 deletions src/s2/util/endian/endian.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,83 @@

#include <cstdint>

#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<uint16_t>((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
//
Expand All @@ -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.
Expand Down Expand Up @@ -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 <typename T>
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;
}
Expand Down
Loading