Skip to content

Commit 8fa1e09

Browse files
committed
feat: implement endian conversion utilities
1 parent 98984a6 commit 8fa1e09

File tree

2 files changed

+28
-58
lines changed

2 files changed

+28
-58
lines changed

src/iceberg/util/endian.h

Lines changed: 25 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -21,36 +21,43 @@
2121

2222
#include <bit>
2323
#include <concepts>
24+
#include <cstdint>
2425

2526
/// \file iceberg/util/endian.h
2627
/// \brief Endianness conversion utilities
2728

2829
namespace iceberg {
2930

30-
/// \brief Concept for values that can be written in little-endian format.
31+
/// \brief Concept for values that can be converted to/from another endian format.
3132
template <typename T>
3233
concept EndianConvertible = std::is_arithmetic_v<T> && !std::same_as<T, bool>;
3334

35+
/// \brief Byte-swap a value. For floating-point types, only support 32-bit and 64-bit
36+
/// floats.
37+
template <EndianConvertible T>
38+
constexpr T ByteSwap(T value) {
39+
if constexpr (sizeof(T) <= 1) {
40+
return value;
41+
} else if constexpr (std::is_integral_v<T>) {
42+
return std::byteswap(value);
43+
} else if constexpr (std::is_floating_point_v<T>) {
44+
if constexpr (sizeof(T) == sizeof(uint32_t)) {
45+
return std::bit_cast<T>(std::byteswap(std::bit_cast<uint32_t>(value)));
46+
} else if constexpr (sizeof(T) == sizeof(uint64_t)) {
47+
return std::bit_cast<T>(std::byteswap(std::bit_cast<uint64_t>(value)));
48+
} else {
49+
static_assert(false, "Unsupported floating-point size for endian conversion.");
50+
}
51+
}
52+
}
53+
3454
/// \brief Convert a value to little-endian format.
3555
template <EndianConvertible T>
3656
constexpr T ToLittleEndian(T value) {
3757
if constexpr (std::endian::native == std::endian::little || sizeof(T) <= 1) {
3858
return value;
3959
} else {
40-
if constexpr (std::is_integral_v<T>) {
41-
return std::byteswap(value);
42-
} else if constexpr (std::is_floating_point_v<T>) {
43-
// For floats, use the bit_cast -> byteswap -> bit_cast pattern.
44-
if constexpr (sizeof(T) == sizeof(uint32_t)) {
45-
uint32_t int_representation = std::bit_cast<uint32_t>(value);
46-
int_representation = std::byteswap(int_representation);
47-
return std::bit_cast<T>(int_representation);
48-
} else if constexpr (sizeof(T) == sizeof(uint64_t)) {
49-
uint64_t int_representation = std::bit_cast<uint64_t>(value);
50-
int_representation = std::byteswap(int_representation);
51-
return std::bit_cast<T>(int_representation);
52-
}
53-
}
60+
return ByteSwap(value);
5461
}
5562
}
5663

@@ -60,20 +67,7 @@ constexpr T FromLittleEndian(T value) {
6067
if constexpr (std::endian::native == std::endian::little || sizeof(T) <= 1) {
6168
return value;
6269
} else {
63-
if constexpr (std::is_integral_v<T>) {
64-
return std::byteswap(value);
65-
} else if constexpr (std::is_floating_point_v<T>) {
66-
// For floats, use the bit_cast -> byteswap -> bit_cast pattern.
67-
if constexpr (sizeof(T) == sizeof(uint32_t)) {
68-
uint32_t int_representation = std::bit_cast<uint32_t>(value);
69-
int_representation = std::byteswap(int_representation);
70-
return std::bit_cast<T>(int_representation);
71-
} else if constexpr (sizeof(T) == sizeof(uint64_t)) {
72-
uint64_t int_representation = std::bit_cast<uint64_t>(value);
73-
int_representation = std::byteswap(int_representation);
74-
return std::bit_cast<T>(int_representation);
75-
}
76-
}
70+
return ByteSwap(value);
7771
}
7872
}
7973

@@ -83,19 +77,7 @@ constexpr T ToBigEndian(T value) {
8377
if constexpr (std::endian::native == std::endian::big || sizeof(T) <= 1) {
8478
return value;
8579
} else {
86-
if constexpr (std::is_integral_v<T>) {
87-
return std::byteswap(value);
88-
} else if constexpr (std::is_floating_point_v<T>) {
89-
if constexpr (sizeof(T) == sizeof(uint32_t)) {
90-
uint32_t int_representation = std::bit_cast<uint32_t>(value);
91-
int_representation = std::byteswap(int_representation);
92-
return std::bit_cast<T>(int_representation);
93-
} else if constexpr (sizeof(T) == sizeof(uint64_t)) {
94-
uint64_t int_representation = std::bit_cast<uint64_t>(value);
95-
int_representation = std::byteswap(int_representation);
96-
return std::bit_cast<T>(int_representation);
97-
}
98-
}
80+
return ByteSwap(value);
9981
}
10082
}
10183

@@ -105,19 +87,7 @@ constexpr T FromBigEndian(T value) {
10587
if constexpr (std::endian::native == std::endian::big || sizeof(T) <= 1) {
10688
return value;
10789
} else {
108-
if constexpr (std::is_integral_v<T>) {
109-
return std::byteswap(value);
110-
} else if constexpr (std::is_floating_point_v<T>) {
111-
if constexpr (sizeof(T) == sizeof(uint32_t)) {
112-
uint32_t int_representation = std::bit_cast<uint32_t>(value);
113-
int_representation = std::byteswap(int_representation);
114-
return std::bit_cast<T>(int_representation);
115-
} else if constexpr (sizeof(T) == sizeof(uint64_t)) {
116-
uint64_t int_representation = std::bit_cast<uint64_t>(value);
117-
int_representation = std::byteswap(int_representation);
118-
return std::bit_cast<T>(int_representation);
119-
}
120-
}
90+
return ByteSwap(value);
12191
}
12292
}
12393

test/CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,11 @@ add_iceberg_test(json_serde_test
8686

8787
add_iceberg_test(util_test
8888
SOURCES
89-
formatter_test.cc
9089
config_test.cc
91-
visit_type_test.cc
90+
endian_test.cc
91+
formatter_test.cc
9292
string_utils_test.cc
93-
endian_test.cc)
93+
visit_type_test.cc)
9494

9595
if(ICEBERG_BUILD_BUNDLE)
9696
add_iceberg_test(avro_test

0 commit comments

Comments
 (0)