ugmisc 0.2-86
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 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#pragma once
25
39
40#include <cstdint>
41#include <type_traits>
42#include <utility>
43
44#include "ugmisc/features.hpp"
46
47
48
49
50#ifdef UGMISC_HAVE_BITOPS
51# include <bit>
52#endif
53
54
55namespace ugmisc {
56
57
58
59
60/*
61 * Forward declarations.
62 *
63 * We declare these two ways:
64 *
65 * 1. With concepts if concepts are supported OR if we are building the docs.
66 * 2. With enable_if based return types otherwise.
67 *
68 * The declaration used is defined as a macro for use in the definition later.
69 * Just using the option without concepts everywhere would work, but users of
70 * the library may get more helpful error messages where the concepts have been
71 * used.
72 */
73
74#ifdef UGMISC_USE_CONCEPT_DECLS
79template<BitwiseUint T> constexpr T clz(T) noexcept;
80
85template<BitwiseUint T> constexpr T cl1(T) noexcept;
86
91template<BitwiseUint T> constexpr T crz(T) noexcept;
92
97template<BitwiseUint T> constexpr T cr1(T) noexcept;
98
104template<BitwiseUint T> constexpr T bitwidth(T) noexcept;
105
106# ifndef UGMISC_DOCS
107# define UGMISC__clz(x) template<BitwiseUint T> constexpr T clz(T x) noexcept
108# define UGMISC__bitwidth(x) template<BitwiseUint T> constexpr T bitwidth(T x) noexcept
109# define UGMISC__cr1(x) template<BitwiseUint T> constexpr T cr1(T x) noexcept
110# define UGMISC__crz(x) template<BitwiseUint T> constexpr T crz(T x) noexcept
111# define UGMISC__cl1(x) template<BitwiseUint T> constexpr T cl1(T x) noexcept
112# endif
113
114#else
115
116template<class T> constexpr bitwise_uint_only<T> clz(T) noexcept;
117template<class T> constexpr bitwise_uint_only<T> cl1(T) noexcept;
118template<class T> constexpr bitwise_uint_only<T> crz(T) noexcept;
119template<class T> constexpr bitwise_uint_only<T> cr1(T) noexcept;
120template<class T> constexpr bitwise_uint_only<T> bitwidth(T) noexcept;
121
122# ifndef UGMISC_DOCS
123# define UGMISC__clz(x) template<class T> constexpr bitwise_uint_only<T> clz(T x) noexcept
124# define UGMISC__cl1(x) template<class T> constexpr bitwise_uint_only<T> cl1(T x) noexcept
125# define UGMISC__crz(x) template<class T> constexpr bitwise_uint_only<T> crz(T x) noexcept
126# define UGMISC__cr1(x) template<class T> constexpr bitwise_uint_only<T> cr1(T x) noexcept
127# define UGMISC__bitwidth(x) template<class T> constexpr bitwise_uint_only<T> bitwidth(T x) noexcept
128# endif
129
130#endif
131
132
133
134
135#ifdef UGMISC_HAVE_BITOPS
136
137/*
138 * We have all the functions in the standard library, so ours are simple wrappers.
139 *
140 * The UGMISC__* macros were defined before because we use different
141 * declarations for C++20 and C++17, so we can use concepts.
142 */
143
144#ifndef UGMISC_DOCS
145 UGMISC__clz(x) { return std::countl_zero(x); }
146 UGMISC__cl1(x) { return std::countl_one(x); }
147 UGMISC__crz(x) { return std::countr_zero(x); }
148 UGMISC__cr1(x) { return std::countr_one(x); }
149 UGMISC__bitwidth(x) { return std::bit_width(x); }
150#endif
151
152
153#else
154
155/*
156 * We don't have std::countl_zero, std::countr_one etc.
157 *
158 * TODO: Detect or guess compiler intrinsics and use those.
159 */
160
161
162
163
165namespace bitops_ {
166
167
168enum BitFunc { CLZ, CR1, CL1, CRZ };
169
170
171
172
173/*
174 * In the absence of std bitops functions, this generic function partitions the
175 * word into big chunks until it finds one with all ones or all zeros, one the
176 * left or right as required.
177 */
178template<BitFunc F, class T, T Bits = std::numeric_limits<T>::digits, T Shift = 0 >
179static constexpr bitwise_uint_only<T> cxx(T v) noexcept {
180 constexpr T max_bits = std::numeric_limits<T>::digits;
181 constexpr T all_ones = ~((T)0);
182 constexpr T mask_rightshifted = (all_ones >> (max_bits - Bits));
183
184 static_assert( Bits >= 1 );
185 static_assert( Bits + Shift <= max_bits );
186
187 constexpr bool from_left = F == CLZ || F == CL1;
188 constexpr bool count_ones = F == CL1 || F == CR1;
189 constexpr bool count_zeros = ! count_ones;
190 constexpr T mask = mask_rightshifted << Shift;
191
192 bool all_set = (mask & v) == mask;
193 bool all_clear = (mask & v) == 0;
194 bool all_bits_of_counted_state = (count_ones && all_set) || (count_zeros && all_clear);
195 bool all_bits_of_uncounted_state = (count_ones && all_clear) || (count_ones && all_set);
196 bool trivial_result = all_bits_of_counted_state || all_bits_of_uncounted_state;
197
198 /*
199 * First we want to know if all the bits were set or all clear, so we can
200 * trivially return a result. If that fails we delegate to two functions
201 * which examine a smaller part of the word each. Each function is an
202 * instance of this function template.
203 *
204 * These functions cannot be instantiated if either of them will have 0
205 * Bits. So we must keep the delegating part of our function shielded by a
206 * constexpr if condition.
207 */
208
209 if constexpr ( Bits == 1 ) {
210 return all_bits_of_counted_state ? 1 : 0;
211 } else {
212 if ( trivial_result ) {
213 return all_bits_of_counted_state ? Bits : 0;
214 }
215
216 constexpr T left_partition_bits = Bits/2;
217 constexpr T right_partition_bits = Bits - left_partition_bits;
218 constexpr T left_partition_shift = Shift + right_partition_bits;
219 constexpr T right_partition_shift = Shift;
220
221 constexpr T first_partition_bits
222 = from_left ? left_partition_bits : right_partition_bits;
223 constexpr T second_partition_bits
224 = from_left ? right_partition_bits : left_partition_bits;
225
226 constexpr T first_partition_shift
227 = from_left ? left_partition_shift : right_partition_shift;
228 constexpr T second_partition_shift
229 = from_left ? right_partition_shift : left_partition_shift;
230
231 T first_result = cxx<F, T, first_partition_bits, first_partition_shift>(v);
232
233 if ( first_result < first_partition_bits ) {
234 return first_result;
235 } else {
236 T second_result = cxx<F, T, second_partition_bits, second_partition_shift>(v);
237 return first_result + second_result;
238 }
239 }
240}
241
242} /* bitops_ (::ugmisc::bitops_) */
244
245
246
247
248#ifndef UGMISC_DOCS
249 UGMISC__clz(x) { return bitops_::cxx<bitops_::CLZ, T>(x); }
250 UGMISC__cl1(x) { return bitops_::cxx<bitops_::CL1, T>(x); }
251 UGMISC__crz(x) { return bitops_::cxx<bitops_::CRZ, T>(x); }
252 UGMISC__cr1(x) { return bitops_::cxx<bitops_::CR1, T>(x); }
253 UGMISC__bitwidth(x) { return std::numeric_limits<T>::digits - clz(x); }
254#endif
255
256
257
258
259
260#endif /* No C++20 library bit funcs. */
261
262
263
264
265} /* ::ugmisc */
constexpr T cl1(T) noexcept
constexpr T clz(T) noexcept
constexpr T cr1(T) noexcept
constexpr T bitwidth(T) noexcept
constexpr T crz(T) noexcept
Feature detection.
Utility required by other ugmisc headers.
std::enable_if_t< is_bitwise_uint< T >, R > bitwise_uint_only
Definition sfinae_helpers.hpp:194