ugmisc 0.2-76
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
64template<class T> constexpr bitwise_uint_only<T> clz(T) noexcept;
65
70template<class T> constexpr bitwise_uint_only<T> cl1(T) noexcept;
71
76template<class T> constexpr bitwise_uint_only<T> crz(T) noexcept;
77
82template<class T> constexpr bitwise_uint_only<T> cr1(T) noexcept;
83
89template<class T> constexpr bitwise_uint_only<T> bitwidth(T) noexcept;
90
91
92
93
94#ifdef UGMISC_HAVE_BITOPS
95
96/*
97 * We have all the functions in the standard library, so ours are simple wrappers.
98 */
99
100template<class T>
101constexpr bitwise_uint_only<T> clz(T x) noexcept { return std::countl_zero(x); }
102
103template<class T>
104constexpr bitwise_uint_only<T> cl1(T x) noexcept { return std::countl_one(x); }
105
106template<class T>
107constexpr bitwise_uint_only<T> crz(T x) noexcept { return std::countr_zero(x); }
108
109template<class T>
110constexpr bitwise_uint_only<T> cr1(T x) noexcept { return std::countr_one(x); }
111
112template<class T>
113constexpr bitwise_uint_only<T> bitwidth(T x) noexcept {
114 return std::bit_width(x);
115}
116
117
118#else
119
120/*
121 * We don't have std::countl_zero, std::countr_one etc.
122 *
123 * TODO: Detect or guess compiler intrinsics and use those.
124 */
125
126
127
128
130namespace bitops_ {
131
132
133enum BitFunc { CLZ, CR1, CL1, CRZ };
134
135
136
137
138/*
139 * In the absence of std bitops functions, this generic function partitions the
140 * word into big chunks until it finds one with all ones or all zeros, one the
141 * left or right as required.
142 */
143template<BitFunc F, class T, T Bits = std::numeric_limits<T>::digits, T Shift = 0 >
144static constexpr bitwise_uint_only<T> cxx(T v) noexcept {
145 constexpr T max_bits = std::numeric_limits<T>::digits;
146 constexpr T all_ones = ~((T)0);
147 constexpr T mask_rightshifted = (all_ones >> (max_bits - Bits));
148
149 static_assert( Bits >= 1 );
150 static_assert( Bits + Shift <= max_bits );
151
152 constexpr bool from_left = F == CLZ || F == CL1;
153 constexpr bool count_ones = F == CL1 || F == CR1;
154 constexpr bool count_zeros = ! count_ones;
155 constexpr T mask = mask_rightshifted << Shift;
156
157 bool all_set = (mask & v) == mask;
158 bool all_clear = (mask & v) == 0;
159 bool all_bits_of_counted_state = (count_ones && all_set) || (count_zeros && all_clear);
160 bool all_bits_of_uncounted_state = (count_ones && all_clear) || (count_ones && all_set);
161 bool trivial_result = all_bits_of_counted_state || all_bits_of_uncounted_state;
162
163 /*
164 * First we want to know if all the bits were set or all clear, so we can
165 * trivially return a result. If that fails we delegate to two functions
166 * which examine a smaller part of the word each. Each function is an
167 * instance of this function template.
168 *
169 * These functions cannot be instantiated if either of them will have 0
170 * Bits. So we must keep the delegating part of our function shielded by a
171 * constexpr if condition.
172 */
173
174 if constexpr ( Bits == 1 ) {
175 return all_bits_of_counted_state ? 1 : 0;
176 } else {
177 if ( trivial_result ) {
178 return all_bits_of_counted_state ? Bits : 0;
179 }
180
181 constexpr T left_partition_bits = Bits/2;
182 constexpr T right_partition_bits = Bits - left_partition_bits;
183 constexpr T left_partition_shift = Shift + right_partition_bits;
184 constexpr T right_partition_shift = Shift;
185
186 constexpr T first_partition_bits
187 = from_left ? left_partition_bits : right_partition_bits;
188 constexpr T second_partition_bits
189 = from_left ? right_partition_bits : left_partition_bits;
190
191 constexpr T first_partition_shift
192 = from_left ? left_partition_shift : right_partition_shift;
193 constexpr T second_partition_shift
194 = from_left ? right_partition_shift : left_partition_shift;
195
196 T first_result = cxx<F, T, first_partition_bits, first_partition_shift>(v);
197
198 if ( first_result < first_partition_bits ) {
199 return first_result;
200 } else {
201 T second_result = cxx<F, T, second_partition_bits, second_partition_shift>(v);
202 return first_result + second_result;
203 }
204 }
205}
206
207} /* bitops_ (::ugmisc::bitops_) */
209
210
211
212
213template<class T> constexpr bitwise_uint_only<T> clz(T v) noexcept {
214 return bitops_::cxx<bitops_::CLZ, T>(v);
215}
216
217template<class T> constexpr bitwise_uint_only<T> cl1(T v) noexcept {
218 return bitops_::cxx<bitops_::CL1, T>(v);
219}
220
221
222template<class T> constexpr bitwise_uint_only<T> crz(T v) noexcept {
223 return bitops_::cxx<bitops_::CRZ, T>(v);
224}
225
226
227template<class T> constexpr bitwise_uint_only<T> cr1(T v) noexcept {
228 return bitops_::cxx<bitops_::CR1, T>(v);
229}
230
231
232
233
234template<class T>
235constexpr bitwise_uint_only<T> bitwidth(T x) noexcept {
236 return std::numeric_limits<T>::digits - clz(x);
237}
238
239
240
241
242
243#endif /* No C++20 library bit funcs. */
244
245
246
247
248} /* ::ugmisc */
constexpr bitwise_uint_only< T > clz(T) noexcept
Definition bitops.hpp:213
constexpr bitwise_uint_only< T > cl1(T) noexcept
Definition bitops.hpp:217
constexpr bitwise_uint_only< T > cr1(T) noexcept
Definition bitops.hpp:227
constexpr bitwise_uint_only< T > crz(T) noexcept
Definition bitops.hpp:222
constexpr bitwise_uint_only< T > bitwidth(T) noexcept
Definition bitops.hpp:235
Feature detection.
Utility required by other ugmisc headers.
std::enable_if_t< std::numeric_limits< T >::is_integer &&(! std::numeric_limits< T >::is_signed) &&(std::numeric_limits< T >::radix==2) &&std::numeric_limits< T >::is_modulo &&std::is_arithmetic_v< T > &&is_bitwise< T >, R > bitwise_uint_only
Definition sfinae_helpers.hpp:140