Skip to content

Commit 25bce12

Browse files
derekmaurocopybara-github
authored andcommitted
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
1 parent 70ba73e commit 25bce12

File tree

6 files changed

+150
-26
lines changed

6 files changed

+150
-26
lines changed

absl/base/config.h

+3
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,9 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
466466
//
467467
// Checks the endianness of the platform.
468468
//
469+
// Prefer using `std::endian` in C++20, or `absl::endian` from
470+
// absl/numeric/bits.h prior to C++20.
471+
//
469472
// Notes: uses the built in endian macros provided by GCC (since 4.6) and
470473
// Clang (since 3.2); see
471474
// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html.

absl/base/internal/endian.h

+22-26
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414
//
15+
// This file is for Abseil internal use only.
16+
// See //absl/numeric/bits.h for supported functions related to endian-ness.
1517

1618
#ifndef ABSL_BASE_INTERNAL_ENDIAN_H_
1719
#define ABSL_BASE_INTERNAL_ENDIAN_H_
@@ -28,44 +30,38 @@
2830
namespace absl {
2931
ABSL_NAMESPACE_BEGIN
3032

31-
inline uint64_t gbswap_64(uint64_t host_int) {
33+
constexpr uint64_t gbswap_64(uint64_t x) {
3234
#if ABSL_HAVE_BUILTIN(__builtin_bswap64) || defined(__GNUC__)
33-
return __builtin_bswap64(host_int);
34-
#elif defined(_MSC_VER)
35-
return _byteswap_uint64(host_int);
35+
return __builtin_bswap64(x);
3636
#else
37-
return (((host_int & uint64_t{0xFF}) << 56) |
38-
((host_int & uint64_t{0xFF00}) << 40) |
39-
((host_int & uint64_t{0xFF0000}) << 24) |
40-
((host_int & uint64_t{0xFF000000}) << 8) |
41-
((host_int & uint64_t{0xFF00000000}) >> 8) |
42-
((host_int & uint64_t{0xFF0000000000}) >> 24) |
43-
((host_int & uint64_t{0xFF000000000000}) >> 40) |
44-
((host_int & uint64_t{0xFF00000000000000}) >> 56));
37+
return (((x & uint64_t{0xFF}) << 56) |
38+
((x & uint64_t{0xFF00}) << 40) |
39+
((x & uint64_t{0xFF0000}) << 24) |
40+
((x & uint64_t{0xFF000000}) << 8) |
41+
((x & uint64_t{0xFF00000000}) >> 8) |
42+
((x & uint64_t{0xFF0000000000}) >> 24) |
43+
((x & uint64_t{0xFF000000000000}) >> 40) |
44+
((x & uint64_t{0xFF00000000000000}) >> 56));
4545
#endif
4646
}
4747

48-
inline uint32_t gbswap_32(uint32_t host_int) {
48+
constexpr uint32_t gbswap_32(uint32_t x) {
4949
#if ABSL_HAVE_BUILTIN(__builtin_bswap32) || defined(__GNUC__)
50-
return __builtin_bswap32(host_int);
51-
#elif defined(_MSC_VER)
52-
return _byteswap_ulong(host_int);
50+
return __builtin_bswap32(x);
5351
#else
54-
return (((host_int & uint32_t{0xFF}) << 24) |
55-
((host_int & uint32_t{0xFF00}) << 8) |
56-
((host_int & uint32_t{0xFF0000}) >> 8) |
57-
((host_int & uint32_t{0xFF000000}) >> 24));
52+
return (((x & uint32_t{0xFF}) << 24) |
53+
((x & uint32_t{0xFF00}) << 8) |
54+
((x & uint32_t{0xFF0000}) >> 8) |
55+
((x & uint32_t{0xFF000000}) >> 24));
5856
#endif
5957
}
6058

61-
inline uint16_t gbswap_16(uint16_t host_int) {
59+
constexpr uint16_t gbswap_16(uint16_t x) {
6260
#if ABSL_HAVE_BUILTIN(__builtin_bswap16) || defined(__GNUC__)
63-
return __builtin_bswap16(host_int);
64-
#elif defined(_MSC_VER)
65-
return _byteswap_ushort(host_int);
61+
return __builtin_bswap16(x);
6662
#else
67-
return (((host_int & uint16_t{0xFF}) << 8) |
68-
((host_int & uint16_t{0xFF00}) >> 8));
63+
return (((x & uint16_t{0xFF}) << 8) |
64+
((x & uint16_t{0xFF00}) >> 8));
6965
#endif
7066
}
7167

absl/numeric/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ cc_library(
4141
deps = [
4242
"//absl/base:config",
4343
"//absl/base:core_headers",
44+
"//absl/base:endian",
4445
],
4546
)
4647

absl/numeric/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ absl_cc_library(
2323
COPTS
2424
${ABSL_DEFAULT_COPTS}
2525
DEPS
26+
absl::config
2627
absl::core_headers
28+
absl::endian
2729
PUBLIC
2830
)
2931

absl/numeric/bits.h

+66
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1355r2.html
2828
// P1956R1:
2929
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1956r1.pdf
30+
// P0463R1
31+
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0463r1.html
32+
// P1272R4
33+
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1272r4.html
3034
//
3135
// When using a standard library that implements these functions, we use the
3236
// standard library's implementation.
@@ -45,6 +49,7 @@
4549
#endif
4650

4751
#include "absl/base/attributes.h"
52+
#include "absl/base/internal/endian.h"
4853
#include "absl/numeric/internal/bits.h"
4954

5055
namespace absl {
@@ -190,6 +195,67 @@ ABSL_INTERNAL_CONSTEXPR_CLZ inline
190195

191196
#endif
192197

198+
#if defined(__cpp_lib_endian) && __cpp_lib_endian >= 201907L
199+
200+
// https://en.cppreference.com/w/cpp/types/endian
201+
//
202+
// Indicates the endianness of all scalar types:
203+
// * If all scalar types are little-endian, `absl::endian::native` equals
204+
// absl::endian::little.
205+
// * If all scalar types are big-endian, `absl::endian::native` equals
206+
// `absl::endian::big`.
207+
// * Platforms that use anything else are unsupported.
208+
using std::endian;
209+
210+
#else
211+
212+
enum class endian {
213+
little,
214+
big,
215+
#if defined(ABSL_IS_LITTLE_ENDIAN)
216+
native = little
217+
#elif defined(ABSL_IS_BIG_ENDIAN)
218+
native = big
219+
#else
220+
#error "Endian detection needs to be set up for this platform"
221+
#endif
222+
};
223+
224+
#endif // defined(__cpp_lib_endian) && __cpp_lib_endian >= 201907L
225+
226+
#if defined(__cpp_lib_byteswap) && __cpp_lib_byteswap >= 202110L
227+
228+
// https://en.cppreference.com/w/cpp/numeric/byteswap
229+
//
230+
// Reverses the bytes in the given integer value `x`.
231+
//
232+
// `absl::byteswap` participates in overload resolution only if `T` satisfies
233+
// integral, i.e., `T` is an integer type. The program is ill-formed if `T` has
234+
// padding bits.
235+
using std::byteswap;
236+
237+
#else
238+
239+
template <class T>
240+
[[nodiscard]] constexpr T byteswap(T x) noexcept {
241+
static_assert(std::is_integral_v<T>,
242+
"byteswap requires an integral argument");
243+
static_assert(
244+
sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8,
245+
"byteswap works only with 8, 16, 32, or 64-bit integers");
246+
if constexpr (sizeof(T) == 1) {
247+
return x;
248+
} else if constexpr (sizeof(T) == 2) {
249+
return static_cast<T>(gbswap_16(static_cast<uint16_t>(x)));
250+
} else if constexpr (sizeof(T) == 4) {
251+
return static_cast<T>(gbswap_32(static_cast<uint32_t>(x)));
252+
} else if constexpr (sizeof(T) == 8) {
253+
return static_cast<T>(gbswap_64(static_cast<uint64_t>(x)));
254+
}
255+
}
256+
257+
#endif // defined(__cpp_lib_byteswap) && __cpp_lib_byteswap >= 202110L
258+
193259
ABSL_NAMESPACE_END
194260
} // namespace absl
195261

absl/numeric/bits_test.cc

+56
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "absl/numeric/bits.h"
1616

17+
#include <cstdint>
1718
#include <limits>
1819
#include <type_traits>
1920

@@ -636,6 +637,61 @@ static_assert(ABSL_INTERNAL_HAS_CONSTEXPR_CLZ, "clz should be constexpr");
636637
static_assert(ABSL_INTERNAL_HAS_CONSTEXPR_CTZ, "ctz should be constexpr");
637638
#endif
638639

640+
TEST(Endian, Comparison) {
641+
#if defined(ABSL_IS_LITTLE_ENDIAN)
642+
static_assert(absl::endian::native == absl::endian::little);
643+
static_assert(absl::endian::native != absl::endian::big);
644+
#endif
645+
#if defined(ABSL_IS_BIG_ENDIAN)
646+
static_assert(absl::endian::native != absl::endian::little);
647+
static_assert(absl::endian::native == absl::endian::big);
648+
#endif
649+
}
650+
651+
TEST(Byteswap, Constexpr) {
652+
static_assert(absl::byteswap<int8_t>(0x12) == 0x12);
653+
static_assert(absl::byteswap<int16_t>(0x1234) == 0x3412);
654+
static_assert(absl::byteswap<int32_t>(0x12345678) == 0x78563412);
655+
static_assert(absl::byteswap<int64_t>(0x123456789abcdef0) ==
656+
static_cast<int64_t>(0xf0debc9a78563412));
657+
static_assert(absl::byteswap<uint8_t>(0x21) == 0x21);
658+
static_assert(absl::byteswap<uint16_t>(0x4321) == 0x2143);
659+
static_assert(absl::byteswap<uint32_t>(0x87654321) == 0x21436587);
660+
static_assert(absl::byteswap<uint64_t>(0xfedcba9876543210) ==
661+
static_cast<uint64_t>(0x1032547698badcfe));
662+
static_assert(absl::byteswap<int32_t>(static_cast<int32_t>(0xdeadbeef)) ==
663+
static_cast<int32_t>(0xefbeadde));
664+
}
665+
666+
TEST(Byteswap, NotConstexpr) {
667+
int8_t a = 0x12;
668+
int16_t b = 0x1234;
669+
int32_t c = 0x12345678;
670+
int64_t d = 0x123456789abcdef0;
671+
uint8_t e = 0x21;
672+
uint16_t f = 0x4321;
673+
uint32_t g = 0x87654321;
674+
uint64_t h = 0xfedcba9876543210;
675+
EXPECT_EQ(absl::byteswap<int8_t>(a), 0x12);
676+
EXPECT_EQ(absl::byteswap<int16_t>(b), 0x3412);
677+
EXPECT_EQ(absl::byteswap(c), 0x78563412);
678+
EXPECT_EQ(absl::byteswap(d), 0xf0debc9a78563412);
679+
EXPECT_EQ(absl::byteswap<uint8_t>(e), 0x21);
680+
EXPECT_EQ(absl::byteswap<uint16_t>(f), 0x2143);
681+
EXPECT_EQ(absl::byteswap(g), 0x21436587);
682+
EXPECT_EQ(absl::byteswap(h), 0x1032547698badcfe);
683+
EXPECT_EQ(absl::byteswap(absl::byteswap<int8_t>(a)), a);
684+
EXPECT_EQ(absl::byteswap(absl::byteswap<int16_t>(b)), b);
685+
EXPECT_EQ(absl::byteswap(absl::byteswap(c)), c);
686+
EXPECT_EQ(absl::byteswap(absl::byteswap(d)), d);
687+
EXPECT_EQ(absl::byteswap(absl::byteswap<uint8_t>(e)), e);
688+
EXPECT_EQ(absl::byteswap(absl::byteswap<uint16_t>(f)), f);
689+
EXPECT_EQ(absl::byteswap(absl::byteswap(g)), g);
690+
EXPECT_EQ(absl::byteswap(absl::byteswap(h)), h);
691+
EXPECT_EQ(absl::byteswap<uint32_t>(0xdeadbeef), 0xefbeadde);
692+
EXPECT_EQ(absl::byteswap<const uint32_t>(0xdeadbeef), 0xefbeadde);
693+
}
694+
639695
} // namespace
640696
ABSL_NAMESPACE_END
641697
} // namespace absl

0 commit comments

Comments
 (0)