UgMisc 0.2-110
Miscellaneous C++ header library
Loading...
Searching...
No Matches
bitops.hpp
Go to the documentation of this file.
1/*
2 * SPDX-Licence-Identifier: MIT
3 *
4 * Copyright 2025-2026 Larry Chips
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the “Software”), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#ifndef UGMISC_BITOPS_HPP
25#define UGMISC_BITOPS_HPP
26
40
41#include <cstdint>
42#include <type_traits>
43#include <utility>
44
45#include "ugmisc/features.hpp"
47
48#ifdef UGMISC_HAVE_BITOPS
49# include <bit>
50#endif
51
52
53namespace ugmisc {
54
55
56
57
58/*
59 * Forward declarations.
60 *
61 * We declare these two ways:
62 *
63 * 1. With concepts if concepts are supported OR if we are building the docs.
64 * 2. With enable_if based return types otherwise.
65 *
66 * This is accomplished using the UGMISC_BITWISE_UINT_RETURN macro from the
67 * sfinae_helpers.hpp header.
68 */
69
74template<class T> constexpr auto clz(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int);
75
80template<class T> constexpr auto cl1(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int);
81
86template<class T> constexpr auto crz(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int);
87
92template<class T> constexpr auto cr1(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int);
93
99template<class T> constexpr auto bitwidth(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int);
100
101
102
103
105namespace _bitops {
106
107
108enum BitFunc { CLZ, CR1, CL1, CRZ };
109
110
111
112
113/*
114 * In the absence of std bitops functions, this generic function partitions the
115 * word into big chunks until it finds one with all ones or all zeros, on the
116 * left or right as required.
117 */
118template<BitFunc F, class T, int Bits = std::numeric_limits<T>::digits, int Shift = 0 >
119static constexpr auto cxx(T v) noexcept
121{
122 constexpr int max_bits = std::numeric_limits<T>::digits;
123 constexpr T all_ones = ~((T)0);
124 constexpr T mask_rightshifted = (all_ones >> (max_bits - Bits));
125
126 static_assert( Bits >= 1 );
127 static_assert( Bits + Shift <= max_bits );
128
129 constexpr bool from_left = F == CLZ || F == CL1;
130 constexpr bool count_ones = F == CL1 || F == CR1;
131 constexpr bool count_zeros = ! count_ones;
132 constexpr T mask = mask_rightshifted << Shift;
133
134 bool all_set = (mask & v) == mask;
135 bool all_clear = (mask & v) == 0;
136 bool all_bits_of_counted_state = (count_ones && all_set) || (count_zeros && all_clear);
137 bool all_bits_of_uncounted_state = (count_ones && all_clear) || (count_ones && all_set);
138 bool trivial_result = all_bits_of_counted_state || all_bits_of_uncounted_state;
139
140 /*
141 * First we want to know if all the bits were set or all clear, so we can
142 * trivially return a result. If that fails we delegate to two functions
143 * which examine a smaller part of the word each. Each function is an
144 * instance of this function template.
145 *
146 * These functions cannot be instantiated if either of them will have 0
147 * Bits. So we must keep the delegating part of our function shielded by a
148 * constexpr if condition.
149 */
150
151 if constexpr ( Bits == 1 ) {
152 return all_bits_of_counted_state ? 1 : 0;
153 } else {
154 if ( trivial_result ) {
155 return all_bits_of_counted_state ? Bits : 0;
156 }
157
158 constexpr int left_partition_bits = Bits/2;
159 constexpr int right_partition_bits = Bits - left_partition_bits;
160 constexpr int left_partition_shift = Shift + right_partition_bits;
161 constexpr int right_partition_shift = Shift;
162
163 constexpr int first_partition_bits
164 = from_left ? left_partition_bits : right_partition_bits;
165 constexpr int second_partition_bits
166 = from_left ? right_partition_bits : left_partition_bits;
167
168 constexpr int first_partition_shift
169 = from_left ? left_partition_shift : right_partition_shift;
170 constexpr int second_partition_shift
171 = from_left ? right_partition_shift : left_partition_shift;
172
173 int first_result = cxx<F, T, first_partition_bits, first_partition_shift>(v);
174
175 if ( first_result < first_partition_bits ) {
176 return first_result;
177 } else {
178 int second_result = cxx<F, T, second_partition_bits, second_partition_shift>(v);
179 return first_result + second_result;
180 }
181 }
182}
183
188template<class T, class=int> constexpr bool is_std_clz_type = false;
189template<class T, class=int> constexpr bool is_std_crz_type = false;
190template<class T, class=int> constexpr bool is_std_cl1_type = false;
191template<class T, class=int> constexpr bool is_std_cr1_type = false;
192template<class T, class=int> constexpr bool is_std_bitwidth_type = false;
193#ifdef UGMISC_HAVE_BITOPS
194template<class T>
195constexpr bool
196is_std_clz_type<T, decltype(::std::countl_zero(std::declval<T>()))>
197= true;
198
199template<class T>
200constexpr bool
201is_std_crz_type<T, decltype(::std::countr_zero(std::declval<T>()))>
202= true;
203
204template<class T>
205constexpr bool
206is_std_cl1_type<T, decltype(::std::countl_one(std::declval<T>()))>
207= true;
208
209template<class T>
210constexpr bool
211is_std_cr1_type<T, decltype(::std::countr_one(std::declval<T>()))>
212= true;
213
214template<class T>
215constexpr bool
216is_std_bitwidth_type<T, decltype(::std::bit_width(std::declval<T>()))>
217= true;
218#endif
219
224
225/*
226 * The generic call_* templates call cxx. The partially specialised ones call
227 * std functions.
228 */
229template<class T, bool=true>
230struct call_clz {
231 constexpr int operator()(T x) const noexcept {
232 return cxx<_bitops::CLZ, T>(x);
233 }
234};
235
236template<class T, bool=true>
237struct call_cl1 {
238 constexpr int operator()(T x) const noexcept {
239 return cxx<_bitops::CL1, T>(x);
240 }
241};
242
243template<class T, bool=true>
244struct call_crz {
245 constexpr int operator()(T x) const noexcept {
246 return cxx<_bitops::CRZ, T>(x);
247 }
248};
249
250template<class T, bool=true>
251struct call_cr1 {
252 constexpr int operator()(T x) const noexcept {
253 return cxx<_bitops::CR1, T>(x);
254 }
255};
256
257template<class T, bool=true>
258struct call_bitwidth {
259 constexpr int operator()(T x) const noexcept {
260 return std::numeric_limits<T>::digits - clz(x);
261 }
262};
263
264#ifdef UGMISC_HAVE_BITOPS
265template<class T>
266struct call_clz<T, is_std_clz_type<T>> {
267 constexpr int operator()(T x) const noexcept {
268 return ::std::countl_zero(x);
269 }
270};
271
272template<class T>
273struct call_cl1<T, is_std_clz_type<T>> {
274 constexpr int operator()(T x) const noexcept {
275 return ::std::countl_one(x);
276 }
277};
278
279template<class T>
280struct call_crz<T, is_std_clz_type<T>> {
281 constexpr int operator()(T x) const noexcept {
282 return ::std::countr_zero(x);
283 }
284};
285
286template<class T>
287struct call_cr1<T, is_std_clz_type<T>> {
288 constexpr int operator()(T x) const noexcept {
289 return ::std::countr_one(x);
290 }
291};
292
293template<class T>
294struct call_bitwidth<T, is_std_clz_type<T>> {
295 constexpr int operator()(T x) const noexcept {
296 return ::std::bit_width(x);
297 }
298};
299#endif
300} /* _bitops (::ugmisc::_bitops) */
302
303
304
305
306/*
307 * Definitions.
308 */
309template<class T>
310constexpr auto clz(T x) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int) {
311 return _bitops::call_clz<T>{}(x);
312}
313
314template<class T>
315constexpr auto cl1(T x) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int) {
316 return _bitops::call_cl1<T>{}(x);
317}
318
319template<class T>
320constexpr auto crz(T x) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int) {
321 return _bitops::call_crz<T>{}(x);
322}
323
324template<class T>
325constexpr auto cr1(T x) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int) {
326 return _bitops::call_cr1<T>{}(x);
327}
328
329template<class T>
330constexpr auto bitwidth(T x) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int) {
331 return _bitops::call_bitwidth<T>{}(x);
332}
333} /* ::ugmisc */
334
335#endif /* UGMISC_BITOPS_HPP */
constexpr auto clz(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
Definition bitops.hpp:310
constexpr auto crz(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
Definition bitops.hpp:320
constexpr auto bitwidth(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
Definition bitops.hpp:330
constexpr auto cl1(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
Definition bitops.hpp:315
constexpr auto cr1(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
Definition bitops.hpp:325
Feature detection.
Utility required by other ugmisc headers.
#define UGMISC_BITWISE_UINT_RETURN(T, R)
Definition sfinae_helpers.hpp:224