|
19 | 19 |
|
20 | 20 | #pragma once |
21 | 21 |
|
22 | | -#include <array> |
23 | 22 | #include <bit> |
24 | 23 | #include <concepts> |
| 24 | +#include <cstdint> |
25 | 25 |
|
26 | 26 | /// \file iceberg/util/endian.h |
27 | 27 | /// \brief Endianness conversion utilities |
28 | 28 |
|
29 | 29 | namespace iceberg { |
30 | 30 |
|
31 | | -/// \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. |
32 | 32 | template <typename T> |
33 | | -concept EndianConvertible = std::is_arithmetic_v<T>; |
| 33 | +concept EndianConvertible = std::is_arithmetic_v<T> && !std::same_as<T, bool>; |
34 | 34 |
|
35 | | -/// \brief Concept for values that can be written in big-endian format, |
36 | | -template <typename T> |
37 | | -concept BigEndianWritable = std::same_as<T, std::array<uint8_t, 16>>; |
| 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 | +} |
38 | 53 |
|
39 | 54 | /// \brief Convert a value to little-endian format. |
40 | 55 | template <EndianConvertible T> |
41 | | -T ToLittleEndian(T value) { |
42 | | - if constexpr (std::endian::native != std::endian::little && sizeof(T) > 1) { |
43 | | - return std::byteswap(value); |
| 56 | +constexpr T ToLittleEndian(T value) { |
| 57 | + if constexpr (std::endian::native == std::endian::little || sizeof(T) <= 1) { |
| 58 | + return value; |
| 59 | + } else { |
| 60 | + return ByteSwap(value); |
44 | 61 | } |
45 | | - return value; |
46 | 62 | } |
47 | 63 |
|
48 | 64 | /// \brief Convert a value from little-endian format. |
49 | 65 | template <EndianConvertible T> |
50 | | -T FromLittleEndian(T value) { |
51 | | - if constexpr (std::endian::native != std::endian::little && sizeof(T) > 1) { |
52 | | - return std::byteswap(value); |
| 66 | +constexpr T FromLittleEndian(T value) { |
| 67 | + if constexpr (std::endian::native == std::endian::little || sizeof(T) <= 1) { |
| 68 | + return value; |
| 69 | + } else { |
| 70 | + return ByteSwap(value); |
53 | 71 | } |
54 | | - return value; |
55 | 72 | } |
56 | 73 |
|
| 74 | +/// \brief Convert a value to big-endian format. |
57 | 75 | template <EndianConvertible T> |
58 | 76 | constexpr T ToBigEndian(T value) { |
59 | 77 | if constexpr (std::endian::native == std::endian::big || sizeof(T) <= 1) { |
60 | 78 | return value; |
61 | 79 | } else { |
62 | | - return std::byteswap(value); |
| 80 | + return ByteSwap(value); |
63 | 81 | } |
64 | 82 | } |
65 | 83 |
|
66 | | -/// \brief Convert a value from big-endian format to native. |
| 84 | +/// \brief Convert a value from big-endian format. |
67 | 85 | template <EndianConvertible T> |
68 | 86 | constexpr T FromBigEndian(T value) { |
69 | 87 | if constexpr (std::endian::native == std::endian::big || sizeof(T) <= 1) { |
70 | 88 | return value; |
71 | 89 | } else { |
72 | | - return std::byteswap(value); |
| 90 | + return ByteSwap(value); |
73 | 91 | } |
74 | 92 | } |
75 | 93 |
|
|
0 commit comments