M5Utility 0.0.12 git rev:13bfbd6
Loading...
Searching...
No Matches
byteswap.hpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
3 *
4 * SPDX-License-Identifier: MIT
5 */
10#ifndef M5_UTILITY_STL_BYTESWAP_HPP
11#define M5_UTILITY_STL_BYTESWAP_HPP
12
13#include <stdint.h>
14#include <type_traits>
15#include <cstring> // memcpy
16
17namespace m5 {
18namespace stl {
19
20namespace detail {
21
22template <typename T>
23struct is_int128 : std::false_type { //
24};
25
26#if defined(__SIZEOF_INT128__)
27template <>
28struct is_int128<__int128> : std::true_type { //
29};
30
31template <>
32struct is_int128<unsigned __int128> : std::true_type { //
33};
34#endif
35
36template <typename T>
37struct is_integer_or_enum : std::integral_constant<bool, std::is_integral<typename std::remove_cv<T>::type>::value ||
38 std::is_enum<typename std::remove_cv<T>::type>::value ||
39 is_int128<typename std::remove_cv<T>::type>::value> { //
40};
41
42template <typename T>
43struct is_unsigned_ex : std::is_unsigned<typename std::remove_cv<T>::type> {
44};
45
46#if defined(__SIZEOF_INT128__)
47template <>
48struct is_unsigned_ex<unsigned __int128> : std::true_type {
49};
50#endif
51
52template <typename T>
54 using type = typename std::make_unsigned<T>::type;
55};
56
57template <typename T>
59 using type = typename make_unsigned_impl<typename std::remove_cv<T>::type>::type;
60};
61
62#if defined(__SIZEOF_INT128__)
63template <>
64struct make_unsigned_impl<__int128> {
65 using type = unsigned __int128;
66};
67
68template <>
69struct make_unsigned_impl<unsigned __int128> {
70 using type = unsigned __int128;
71};
72#endif
73
74} // namespace detail
75
77// Portable bit manipulation (always available for testing)
78template <typename U,
79 typename std::enable_if<sizeof(U) == 2 && detail::is_unsigned_ex<U>::value, std::nullptr_t>::type = nullptr>
80inline constexpr U bswap_portable(U x) noexcept
81{
82 return static_cast<U>((static_cast<uint16_t>(x >> 8) & 0x00FFu) | (static_cast<uint16_t>(x << 8) & 0xFF00u));
83}
84
85template <typename U,
86 typename std::enable_if<sizeof(U) == 4 && detail::is_unsigned_ex<U>::value, std::nullptr_t>::type = nullptr>
87inline constexpr U bswap_portable(U x) noexcept
88{
89 return static_cast<U>(((static_cast<uint32_t>(x) >> 24)) | ((static_cast<uint32_t>(x) >> 8) & 0x0000FF00u) |
90 ((static_cast<uint32_t>(x) << 8) & 0x00FF0000u) | ((static_cast<uint32_t>(x) << 24)));
91}
92
93template <typename U,
94 typename std::enable_if<sizeof(U) == 8 && detail::is_unsigned_ex<U>::value, std::nullptr_t>::type = nullptr>
95inline constexpr U bswap_portable(U x) noexcept
96{
97 return static_cast<U>(
98 ((static_cast<uint64_t>(x) >> 56)) | ((static_cast<uint64_t>(x) >> 40) & 0x000000000000FF00ull) |
99 ((static_cast<uint64_t>(x) >> 24) & 0x0000000000FF0000ull) |
100 ((static_cast<uint64_t>(x) >> 8) & 0x00000000FF000000ull) |
101 ((static_cast<uint64_t>(x) << 8) & 0x000000FF00000000ull) |
102 ((static_cast<uint64_t>(x) << 24) & 0x0000FF0000000000ull) |
103 ((static_cast<uint64_t>(x) << 40) & 0x00FF000000000000ull) | ((static_cast<uint64_t>(x) << 56)));
104}
105
106#if defined(__GNUC__) || defined(__clang__)
107// GCC/Clang: use __builtin_bswap for optimal single-instruction output
108template <typename U,
109 typename std::enable_if<sizeof(U) == 2 && detail::is_unsigned_ex<U>::value, std::nullptr_t>::type = nullptr>
110inline constexpr U bswap_fixed(U x) noexcept
111{
112 return static_cast<U>(__builtin_bswap16(static_cast<uint16_t>(x)));
113}
114
115template <typename U,
116 typename std::enable_if<sizeof(U) == 4 && detail::is_unsigned_ex<U>::value, std::nullptr_t>::type = nullptr>
117inline constexpr U bswap_fixed(U x) noexcept
118{
119 return static_cast<U>(__builtin_bswap32(static_cast<uint32_t>(x)));
120}
121
122template <typename U,
123 typename std::enable_if<sizeof(U) == 8 && detail::is_unsigned_ex<U>::value, std::nullptr_t>::type = nullptr>
124inline constexpr U bswap_fixed(U x) noexcept
125{
126 return static_cast<U>(__builtin_bswap64(static_cast<uint64_t>(x)));
127}
128#else
129// Non-GCC/Clang: delegate to portable
130template <typename U, typename std::enable_if<(sizeof(U) >= 2 && sizeof(U) <= 8) && detail::is_unsigned_ex<U>::value,
131 std::nullptr_t>::type = nullptr>
132inline constexpr U bswap_fixed(U x) noexcept
133{
134 return bswap_portable(x);
135}
136#endif
137
138#if defined(__SIZEOF_INT128__)
139template <typename U,
140 typename std::enable_if<sizeof(U) == 16 && detail::is_unsigned_ex<U>::value, std::nullptr_t>::type = nullptr>
141inline constexpr U bswap_portable(U x) noexcept
142{
143 return ((static_cast<U>(bswap_portable<uint64_t>(static_cast<uint64_t>(x))) << 64) |
144 static_cast<U>(bswap_portable<uint64_t>(static_cast<uint64_t>(x >> 64))));
145}
146
147// Detect __builtin_bswap128 availability
148#if defined(__has_builtin)
149#if __has_builtin(__builtin_bswap128)
150#define M5_HAS_BUILTIN_BSWAP128 1
151#endif
152#elif defined(__GNUC__) && (__GNUC__ >= 13)
153#define M5_HAS_BUILTIN_BSWAP128 1
154#endif
155
156template <typename U,
157 typename std::enable_if<sizeof(U) == 16 && detail::is_unsigned_ex<U>::value, std::nullptr_t>::type = nullptr>
158inline constexpr U bswap_fixed(U x) noexcept
159{
160#if defined(M5_HAS_BUILTIN_BSWAP128)
161 return static_cast<U>(__builtin_bswap128(static_cast<unsigned __int128>(x)));
162#else
163 return ((static_cast<U>(bswap_fixed<uint64_t>(static_cast<uint64_t>(x))) << 64) |
164 static_cast<U>(bswap_fixed<uint64_t>(static_cast<uint64_t>(x >> 64))));
165#endif
166}
167#endif
168
169template <typename U,
170 typename std::enable_if<sizeof(U) == 1 && detail::is_unsigned_ex<U>::value, std::nullptr_t>::type = nullptr>
171inline constexpr U bswap_portable(U x) noexcept
172{
173 return x;
174}
175
176template <typename U,
177 typename std::enable_if<sizeof(U) == 1 && detail::is_unsigned_ex<U>::value, std::nullptr_t>::type = nullptr>
178inline constexpr U bswap_fixed(U x) noexcept
179{
180 return x;
181}
182
183template <typename T, typename std::enable_if<detail::is_integer_or_enum<T>::value, std::nullptr_t>::type = nullptr>
184inline constexpr T byteswap_constexpr(T v) noexcept
185{
186 using U = typename detail::make_unsigned_ex<T>::type;
187 return static_cast<T>(bswap_fixed<U>(static_cast<U>(v)));
188}
190
192template <typename T, typename std::enable_if<detail::is_integer_or_enum<T>::value, std::nullptr_t>::type = nullptr>
193inline constexpr T byteswap(T v) noexcept
194{
195 return byteswap_constexpr(v);
196}
197
199template <typename T, typename std::enable_if<detail::is_integer_or_enum<T>::value, std::nullptr_t>::type = nullptr>
200inline constexpr T byteswap_with_portable(T v) noexcept
201{
202 using U = typename detail::make_unsigned_ex<T>::type;
203 return static_cast<T>(bswap_portable<U>(static_cast<U>(v)));
204}
205
206// If the type is not integral , it is prohibited
207template <typename T, typename std::enable_if<!detail::is_integer_or_enum<T>::value, std::nullptr_t>::type = nullptr>
208T byteswap(T) = delete;
209
210} // namespace stl
211} // namespace m5
212
213#endif
constexpr T byteswap_with_portable(T v) noexcept
byteswap using portable bit manipulation (always available, for testing both paths)
Definition byteswap.hpp:200
constexpr T byteswap(T v) noexcept
byteswap for integral type
Definition byteswap.hpp:193
Top level namespace of M5.
Definition base64.cpp:39
STL compatibility functions and classes.
Definition byteswap.hpp:23
Definition byteswap.hpp:39
Definition byteswap.hpp:43
Definition byteswap.hpp:58
Definition byteswap.hpp:53