UgMisc 0.2-128
Miscellaneous C++ header library
Loading...
Searching...
No Matches
bitops.hpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: © 2025 Larry Chips <larry@larrychips.net>
3 * SPDX-Licence-Identifier: MIT
4 */
5#ifndef UGMISC_BITOPS_HPP
6#define UGMISC_BITOPS_HPP
7
27
28#include <cstdint>
29#include <type_traits>
30#include <utility>
31
32#include "ugmisc/features.hpp"
34
35#ifdef UGMISC_HAVE_BITOPS
36# include <bit>
37#endif
38
39
40namespace ugmisc {
41
42
43
44
45/*
46 * Forward declarations.
47 *
48 * We declare these two ways:
49 *
50 * 1. With concepts if concepts are supported OR if we are building the docs.
51 * 2. With enable_if based return types otherwise.
52 *
53 * This is accomplished using the UGMISC_BITWISE_UINT_RETURN macro from the
54 * sfinae_helpers.hpp header.
55 */
56
61template<class T> constexpr auto clz(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int);
62
67template<class T> constexpr auto cl1(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int);
68
73template<class T> constexpr auto crz(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int);
74
79template<class T> constexpr auto cr1(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int);
80
86template<class T> constexpr auto bitwidth(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int);
87
88
89
90
92namespace _bitops {
93
94
95enum BitFunc { CLZ, CR1, CL1, CRZ };
96
97
98
99
100/*
101 * In the absence of std bitops functions, this generic function partitions the
102 * word into big chunks until it finds one with all ones or all zeros, on the
103 * left or right as required.
104 */
105template<BitFunc F, class T, int Bits = std::numeric_limits<T>::digits, int Shift = 0 >
106static constexpr auto cxx(T v) noexcept
108{
109 constexpr int max_bits = std::numeric_limits<T>::digits;
110 constexpr T all_ones = ~((T)0);
111 constexpr T mask_rightshifted = (all_ones >> (max_bits - Bits));
112
113 static_assert( Bits >= 1 );
114 static_assert( Bits + Shift <= max_bits );
115
116 constexpr bool from_left = F == CLZ || F == CL1;
117 constexpr bool count_ones = F == CL1 || F == CR1;
118 constexpr bool count_zeros = ! count_ones;
119 constexpr T mask = mask_rightshifted << Shift;
120
121 bool all_set = (mask & v) == mask;
122 bool all_clear = (mask & v) == 0;
123 bool all_bits_of_counted_state = (count_ones && all_set) || (count_zeros && all_clear);
124 bool all_bits_of_uncounted_state = (count_ones && all_clear) || (count_ones && all_set);
125 bool trivial_result = all_bits_of_counted_state || all_bits_of_uncounted_state;
126
127 /*
128 * First we want to know if all the bits were set or all clear, so we can
129 * trivially return a result. If that fails we delegate to two functions
130 * which examine a smaller part of the word each. Each function is an
131 * instance of this function template.
132 *
133 * These functions cannot be instantiated if either of them will have 0
134 * Bits. So we must keep the delegating part of our function shielded by a
135 * constexpr if condition.
136 */
137
138 if constexpr ( Bits == 1 ) {
139 return all_bits_of_counted_state ? 1 : 0;
140 } else {
141 if ( trivial_result ) {
142 return all_bits_of_counted_state ? Bits : 0;
143 }
144
145 constexpr int left_partition_bits = Bits/2;
146 constexpr int right_partition_bits = Bits - left_partition_bits;
147 constexpr int left_partition_shift = Shift + right_partition_bits;
148 constexpr int right_partition_shift = Shift;
149
150 constexpr int first_partition_bits
151 = from_left ? left_partition_bits : right_partition_bits;
152 constexpr int second_partition_bits
153 = from_left ? right_partition_bits : left_partition_bits;
154
155 constexpr int first_partition_shift
156 = from_left ? left_partition_shift : right_partition_shift;
157 constexpr int second_partition_shift
158 = from_left ? right_partition_shift : left_partition_shift;
159
160 int first_result = cxx<F, T, first_partition_bits, first_partition_shift>(v);
161
162 if ( first_result < first_partition_bits ) {
163 return first_result;
164 } else {
165 int second_result = cxx<F, T, second_partition_bits, second_partition_shift>(v);
166 return first_result + second_result;
167 }
168 }
169}
170
175template<class T, class=int> constexpr bool is_std_clz_type = false;
176template<class T, class=int> constexpr bool is_std_crz_type = false;
177template<class T, class=int> constexpr bool is_std_cl1_type = false;
178template<class T, class=int> constexpr bool is_std_cr1_type = false;
179template<class T, class=int> constexpr bool is_std_bitwidth_type = false;
180#ifdef UGMISC_HAVE_BITOPS
181template<class T>
182constexpr bool
183is_std_clz_type<T, decltype(::std::countl_zero(std::declval<T>()))>
184= true;
185
186template<class T>
187constexpr bool
188is_std_crz_type<T, decltype(::std::countr_zero(std::declval<T>()))>
189= true;
190
191template<class T>
192constexpr bool
193is_std_cl1_type<T, decltype(::std::countl_one(std::declval<T>()))>
194= true;
195
196template<class T>
197constexpr bool
198is_std_cr1_type<T, decltype(::std::countr_one(std::declval<T>()))>
199= true;
200
201template<class T>
202constexpr bool
203is_std_bitwidth_type<T, decltype(::std::bit_width(std::declval<T>()))>
204= true;
205#endif
206
211
212/*
213 * The generic call_* templates call cxx. The partially specialised ones call
214 * std functions.
215 */
216template<class T, bool=true>
217struct call_clz {
218 constexpr int operator()(T x) const noexcept {
219 return cxx<_bitops::CLZ, T>(x);
220 }
221};
222
223template<class T, bool=true>
224struct call_cl1 {
225 constexpr int operator()(T x) const noexcept {
226 return cxx<_bitops::CL1, T>(x);
227 }
228};
229
230template<class T, bool=true>
231struct call_crz {
232 constexpr int operator()(T x) const noexcept {
233 return cxx<_bitops::CRZ, T>(x);
234 }
235};
236
237template<class T, bool=true>
238struct call_cr1 {
239 constexpr int operator()(T x) const noexcept {
240 return cxx<_bitops::CR1, T>(x);
241 }
242};
243
244template<class T, bool=true>
245struct call_bitwidth {
246 constexpr int operator()(T x) const noexcept {
247 return std::numeric_limits<T>::digits - clz(x);
248 }
249};
250
251#ifdef UGMISC_HAVE_BITOPS
252template<class T>
253struct call_clz<T, is_std_clz_type<T>> {
254 constexpr int operator()(T x) const noexcept {
255 return ::std::countl_zero(x);
256 }
257};
258
259template<class T>
260struct call_cl1<T, is_std_clz_type<T>> {
261 constexpr int operator()(T x) const noexcept {
262 return ::std::countl_one(x);
263 }
264};
265
266template<class T>
267struct call_crz<T, is_std_clz_type<T>> {
268 constexpr int operator()(T x) const noexcept {
269 return ::std::countr_zero(x);
270 }
271};
272
273template<class T>
274struct call_cr1<T, is_std_clz_type<T>> {
275 constexpr int operator()(T x) const noexcept {
276 return ::std::countr_one(x);
277 }
278};
279
280template<class T>
281struct call_bitwidth<T, is_std_clz_type<T>> {
282 constexpr int operator()(T x) const noexcept {
283 return ::std::bit_width(x);
284 }
285};
286#endif
287} /* _bitops (::ugmisc::_bitops) */
289
290
291
292
293/*
294 * Definitions.
295 */
296template<class T>
297constexpr auto clz(T x) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int) {
298 return _bitops::call_clz<T>{}(x);
299}
300
301template<class T>
302constexpr auto cl1(T x) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int) {
303 return _bitops::call_cl1<T>{}(x);
304}
305
306template<class T>
307constexpr auto crz(T x) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int) {
308 return _bitops::call_crz<T>{}(x);
309}
310
311template<class T>
312constexpr auto cr1(T x) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int) {
313 return _bitops::call_cr1<T>{}(x);
314}
315
316template<class T>
317constexpr auto bitwidth(T x) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int) {
318 return _bitops::call_bitwidth<T>{}(x);
319}
320} /* ::ugmisc */
321
322#endif /* UGMISC_BITOPS_HPP */
constexpr auto clz(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
Definition bitops.hpp:297
constexpr auto crz(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
Definition bitops.hpp:307
constexpr auto bitwidth(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
Definition bitops.hpp:317
constexpr auto cl1(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
Definition bitops.hpp:302
constexpr auto cr1(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
Definition bitops.hpp:312
Feature detection.
Utility required by other ugmisc headers.
#define UGMISC_BITWISE_UINT_RETURN(T, R)
Definition sfinae_helpers.hpp:201