6
6
* See LICENSE for more information.
7
7
*/
8
8
9
+ // Support header that brings C++20's <bit> header to C++17.
10
+
9
11
#pragma once
10
12
13
+ #include " ccmath/internal/support/ctz.hpp"
14
+
11
15
#include < cstdint>
12
16
#include < type_traits>
13
17
14
- namespace ccm ::helpers ::traits
18
+ namespace ccm ::support ::traits
15
19
{
20
+ // TODO: Later add this into its own header.
16
21
// clang-format off
17
22
template <typename T> struct is_char : std::false_type {};
18
23
template <> struct is_char <char > : std::true_type {};
@@ -25,11 +30,22 @@ namespace ccm::helpers::traits
25
30
template <> struct is_char <signed char > : std::true_type {};
26
31
template <> struct is_char <unsigned char > : std::true_type {};
27
32
template <typename T> inline constexpr bool is_char_v = is_char<T>::value;
33
+
34
+ template <typename T> struct is_unsigned_integer : std::false_type {};
35
+ template <> struct is_unsigned_integer <unsigned char > : std::true_type {};
36
+ template <> struct is_unsigned_integer <unsigned short > : std::true_type {};
37
+ template <> struct is_unsigned_integer <unsigned int > : std::true_type {};
38
+ template <> struct is_unsigned_integer <unsigned long > : std::true_type {};
39
+ template <> struct is_unsigned_integer <unsigned long long > : std::true_type {};
40
+ #if defined(__SIZEOF_INT128__)
41
+ template <> struct is_unsigned_integer <__uint128_t > : std::true_type {};
42
+ #endif
43
+ template <typename T> inline constexpr bool is_unsigned_integer_v = is_unsigned_integer<T>::value;
28
44
// clang-format on
29
45
30
- } // namespace ccm::helpers ::traits
46
+ } // namespace ccm::support ::traits
31
47
32
- namespace ccm ::helpers
48
+ namespace ccm ::support
33
49
{
34
50
35
51
/* *
@@ -49,16 +65,13 @@ namespace ccm::helpers
49
65
return __builtin_bit_cast (To, src);
50
66
}
51
67
52
- template <class T , std:: enable_if_t <std::is_integral_v<T> && std::is_unsigned_v<T> && !ccm::helpers::traits::is_char_v<T> && !std::is_same_v<T, bool >,
53
- bool > = true >
68
+ template <class T ,
69
+ std:: enable_if_t <std::is_integral_v<T> && std::is_unsigned_v<T> && !ccm::support::traits::is_char_v<T> && !std::is_same_v<T, bool >, bool > = true >
54
70
constexpr bool has_single_bit (T x) noexcept
55
71
{
56
72
return x && !(x & (x - 1 ));
57
73
}
58
74
59
-
60
-
61
-
62
75
/* *
63
76
* @brief Helper function to get the top 16-bits of a double.
64
77
* @param x Double to get the bits from.
@@ -75,10 +88,9 @@ namespace ccm::helpers
75
88
}
76
89
77
90
inline constexpr std::uint32_t top12_bits_of_float (float x) noexcept
78
- {
79
- return bit_cast<std::uint32_t >(x) >> 20 ;
80
- }
81
-
91
+ {
92
+ return bit_cast<std::uint32_t >(x) >> 20 ;
93
+ }
82
94
83
95
inline constexpr std::uint64_t double_to_uint64 (double x) noexcept
84
96
{
@@ -96,29 +108,171 @@ namespace ccm::helpers
96
108
}
97
109
98
110
inline constexpr double int64_to_double (std::int64_t x) noexcept
99
- {
100
- return bit_cast<double >(x);
101
- }
111
+ {
112
+ return bit_cast<double >(x);
113
+ }
102
114
103
115
inline constexpr std::uint32_t float_to_uint32 (float x) noexcept
104
116
{
105
117
return bit_cast<std::uint32_t >(x);
106
118
}
107
119
108
120
inline constexpr std::int32_t float_to_int32 (float x) noexcept
109
- {
110
- return bit_cast<std::int32_t >(x);
111
- }
121
+ {
122
+ return bit_cast<std::int32_t >(x);
123
+ }
112
124
113
125
inline constexpr float uint32_to_float (std::uint32_t x) noexcept
114
126
{
115
127
return bit_cast<float >(x);
116
128
}
117
129
118
130
inline constexpr float int32_to_float (std::int32_t x) noexcept
119
- {
120
- return bit_cast<float >(x);
121
- }
131
+ {
132
+ return bit_cast<float >(x);
133
+ }
134
+
135
+ /* *
136
+ * @brief Rotates unsigned integer bits to the right.
137
+ * https://en.cppreference.com/w/cpp/numeric/rotr
138
+ */
139
+ template <class T >
140
+ constexpr T rotr (T t, int cnt) noexcept
141
+ {
142
+ static_assert (ccm::support::traits::is_unsigned_integer_v<T>, " rotr requires an unsigned integer type" );
143
+ const unsigned int dig = std::numeric_limits<T>::digits;
144
+ if ((static_cast <unsigned int >(cnt) % dig) == 0 ) { return t; }
145
+
146
+ if (cnt < 0 )
147
+ {
148
+ cnt *= -1 ;
149
+ return (t << (static_cast <unsigned int >(cnt) % dig)) |
150
+ (t >> (dig - (static_cast <unsigned int >(cnt) % dig))); // rotr with negative cnt is similar to rotl
151
+ }
152
+
153
+ return (t >> (static_cast <unsigned int >(cnt) % dig)) | (t << (dig - (static_cast <unsigned int >(cnt) % dig)));
154
+ }
155
+
156
+ /* *
157
+ * @brief Rotates unsigned integer bits to the left.
158
+ * https://en.cppreference.com/w/cpp/numeric/rotl
159
+ */
160
+ template <class T >
161
+ constexpr T rotl (T t, int cnt) noexcept
162
+ {
163
+ return rotr (t, -cnt);
164
+ }
165
+
166
+ // https://en.cppreference.com/w/cpp/numeric/countr_zero
167
+ template <typename T>
168
+ [[nodiscard]] inline constexpr std::enable_if_t <std::is_unsigned_v<T>, int > countr_zero (T value)
169
+ {
170
+ if (value == 0 ) { return std::numeric_limits<T>::digits; }
171
+
172
+ if constexpr (ccm::support::traits::is_unsigned_integer_v<T>) { return ccm::support::ctz<T>(value); }
173
+
174
+ int ret = 0 ;
175
+ const unsigned int ulldigits = std::numeric_limits<unsigned long long >::digits;
176
+ while (static_cast <unsigned long long >(value) == 0ULL )
177
+ {
178
+ ret += ulldigits;
179
+ value >>= ulldigits;
180
+ }
181
+ return ret + ccm::support::ctz (static_cast <unsigned long long >(value));
182
+ }
183
+
184
+ template <typename T>
185
+ [[nodiscard]] inline constexpr std::enable_if_t <std::is_unsigned_v<T>, int > countr_one (T value)
186
+ {
187
+ return value != std::numeric_limits<T>::max () ? countr_zero (static_cast <T>(~value)) : std::numeric_limits<T>::digits;
188
+ }
189
+
190
+ template <typename T, std::enable_if_t <ccm::support::traits::is_unsigned_integer_v<T>, bool > = true >
191
+ [[nodiscard]] inline constexpr std::enable_if_t <std::is_unsigned_v<T>, int > countl_zero (T value)
192
+ {
193
+ if (value == 0 ) { return std::numeric_limits<T>::digits; }
194
+
195
+ if constexpr (ccm::support::traits::is_unsigned_integer_v<T>) { return std::numeric_limits<T>::digits - ccm::support::ctz<T>(value); }
196
+
197
+ int ret = 0 ;
198
+ int iter = 0 ;
199
+ const unsigned int ulldigits = std::numeric_limits<unsigned long long >::digits;
200
+ while (true )
201
+ {
202
+ value = rotl (value, ulldigits);
203
+ if ((iter = countl_zero (static_cast <unsigned long long >(value))) != ulldigits) // NOLINT
204
+ break ;
205
+ ret += iter;
206
+ }
207
+ return ret + iter;
208
+ }
209
+
210
+ template <typename T, std::enable_if_t <ccm::support::traits::is_unsigned_integer_v<T>, bool > = true >
211
+ [[nodiscard]] inline constexpr std::enable_if_t <std::is_unsigned_v<T>, int > countl_one (T value)
212
+ {
213
+ return value != std::numeric_limits<T>::max () ? countl_zero (static_cast <T>(~value)) : std::numeric_limits<T>::digits;
214
+ }
215
+
216
+ template <typename T>
217
+ [[nodiscard]] inline constexpr std::enable_if_t <std::is_unsigned_v<T>, int > bit_width (T value)
218
+ {
219
+ return std::numeric_limits<T>::digits - countl_zero (value);
220
+ }
221
+
222
+ #if __has_builtin(__builtin_popcountg)
223
+ template <typename T>
224
+ [[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t <cpp::is_unsigned_v<T>, int > popcount (T value)
225
+ {
226
+ return __builtin_popcountg (value);
227
+ }
228
+ #else // !__has_builtin(__builtin_popcountg)
229
+ template <typename T>
230
+ [[nodiscard]] inline constexpr std::enable_if_t <std::is_unsigned_v<T>, int > popcount (T value)
231
+ {
232
+ int count = 0 ;
233
+ for (int i = 0 ; i != std::numeric_limits<T>::digits; ++i)
234
+ {
235
+ if ((value >> i) & 0x1 ) { ++count; }
236
+ }
237
+ return count;
238
+ }
239
+ #endif // __has_builtin(__builtin_popcountg)
240
+
241
+ // If the compiler has builtin's for popcount, the create specializations that use the builtin.
242
+ #if __has_builtin(__builtin_popcount)
243
+ template <>
244
+ [[nodiscard]] inline constexpr int popcount<unsigned char >(unsigned char value)
245
+ {
246
+ return __builtin_popcount (value);
247
+ }
248
+
249
+ template <>
250
+ [[nodiscard]] inline constexpr int popcount<unsigned short >(unsigned short value)
251
+ {
252
+ return __builtin_popcount (value);
253
+ }
122
254
255
+ template <>
256
+ [[nodiscard]] inline constexpr int popcount<unsigned >(unsigned value)
257
+ {
258
+ return __builtin_popcount (value);
259
+ }
260
+ #endif // __has_builtin(__builtin_popcount)
261
+
262
+ #if __has_builtin(__builtin_popcountl)
263
+ template <>
264
+ [[nodiscard]] inline constexpr int popcount<unsigned long >(unsigned long value)
265
+ {
266
+ return __builtin_popcountl (value);
267
+ }
268
+ #endif // __has_builtin(__builtin_popcountl)
269
+
270
+ #if __has_builtin(__builtin_popcountll)
271
+ template <>
272
+ [[nodiscard]] inline constexpr int popcount<unsigned long long >(unsigned long long value)
273
+ {
274
+ return __builtin_popcountll (value);
275
+ }
276
+ #endif // __has_builtin(__builtin_popcountll)
123
277
124
- } // namespace ccm::helpers
278
+ } // namespace ccm::support
0 commit comments