• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PeterCDMcLean / BitLib / 14987214130

13 May 2025 03:02AM UTC coverage: 49.385% (-46.9%) from 96.333%
14987214130

Pull #7

github

web-flow
Merge aad228108 into 09dd4e61e
Pull Request #7: Add github workflow targets for warnings

5155 of 10638 branches covered (48.46%)

Branch coverage included in aggregate %.

39 of 70 new or added lines in 1 file covered. (55.71%)

128 existing lines in 10 files now uncovered.

5325 of 10583 relevant lines covered (50.32%)

3238286.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.09
/include/bitlib/bit-iterator/bit_details.hpp
1
// ============================== BIT DETAILS =============================== //
2
// Project:         The C++ Bit Library
3
// Name:            bit_details.hpp
4
// Description:     Provides common implementation details and helper classes
5
// Creator:         Vincent Reverdy
6
// Contributor(s):  Vincent Reverdy [2015-2017]
7
//                  Bryce Kille [2019]
8
// License:         BSD 3-Clause License
9
// ========================================================================== //
10
#ifndef _BIT_DETAILS_HPP_INCLUDED
11
#define _BIT_DETAILS_HPP_INCLUDED
12
// ========================================================================== //
13

14

15

16
// ================================ PREAMBLE ================================ //
17
// C++ standard library
18
#include <algorithm>
19
#include <cassert>
20
#include <concepts>
21
#include <cstddef>
22
#include <cstdint>
23
#include <iterator>
24
#include <limits>
25
#include <ranges>
26
#include <stdexcept>
27
#include <tuple>
28
#include <type_traits>
29
#include <utility>
30

31
#include "bitlib/bit_concepts.hpp"
32

33
// Project sources
34
// Third-party libraries
35
// Miscellaneous
36
namespace bit {
37
class bit_value;
38
template <class WordType>
39
class bit_reference;
40
template <class Iterator> class bit_iterator;
41
template <class WordType>
42
using bit_pointer = bit_iterator<WordType*>;
43

44
// ========================================================================== //
45

46
/* ***************************** BINARY DIGITS ****************************** */
47
// Binary digits structure definition
48
// Implementation template: only instantiates static_asserts for non-byte types.
49
template <typename T, bool = std::is_same<T, std::byte>::value>
50
struct binary_digits_impl : std::integral_constant<std::size_t, std::numeric_limits<T>::digits>
51
{
52
    static_assert(std::is_integral<T>::value, "Type must be integral");
53
    static_assert(std::is_unsigned<T>::value, "Type must be unsigned");
54
    static_assert(!std::is_same<T, bool>::value, "Type must not be bool");
55
    static_assert(!std::is_same<T, char>::value, "Type must not be char");
56
};
57

58
// Specialization for std::byte.
59
template <>
60
struct binary_digits_impl<std::byte, true> : std::integral_constant<std::size_t, std::numeric_limits<unsigned char>::digits> {};
61

62
// Public interface that removes cv-qualifiers.
63
template <typename UIntType>
64
struct binary_digits : binary_digits_impl<std::remove_cv_t<UIntType>> {};
65

66
// Binary digits value
67
template <class T>
68
constexpr std::size_t binary_digits_v = binary_digits<T>::value;
69
/* ************************************************************************** */
70

71
#if 0
72
template <typename T>
73
using smallest_integral = std::conditional_t<
74
    (sizeof(T) <= sizeof(std::uint8_t)),
75
    std::uint8_t,
76
    std::conditional_t<
77
        (sizeof(T) <= sizeof(std::uint16_t)),
78
        std::uint16_t,
79
        std::conditional_t<
80
            (sizeof(T) <= sizeof(std::uint32_t)),
81
            std::uint32_t,
82
            std::conditional_t<
83
                (sizeof(T) <= sizeof(std::uint64_t)),
84
                std::uint64_t,
85
                T>>>>;
86
#endif
87

88
/* *************** IMPLEMENTATION DETAILS: CV ITERATOR TRAITS *************** */
89
// Cv iterator traits structure definition
90
template <class Iterator>
91
struct _cv_iterator_traits
92
{
93
    // Assertions
94
    private:
95
    using _traits_t = std::iterator_traits<Iterator>;
96
    using _difference_t = typename _traits_t::difference_type;
97
    using _value_t = typename _traits_t::value_type;
98
    using _pointer_t = typename _traits_t::pointer;
99
    using _reference_t = typename _traits_t::reference;
100
    using _category_t =  typename _traits_t::iterator_category;
101
    using _no_pointer_t = typename std::remove_pointer<_pointer_t>::type;
102
    using _no_reference_t = typename std::remove_reference<_reference_t>::type;
103
    using _raw_value_t = typename std::remove_cv<_value_t>::type;
104
    using _raw_pointer_t = typename std::remove_cv<_no_pointer_t>::type;
105
    using _raw_reference_t = typename std::remove_cv<_no_reference_t>::type;
106
    using _cv_value_t = _no_reference_t;
107
    static_assert(std::is_same<_raw_pointer_t, _raw_value_t>::value, "");
108
    static_assert(std::is_same<_raw_reference_t, _raw_value_t>::value, "");
109

110
    // Types
111
    public:
112
    using difference_type = _difference_t;
113
    using value_type = _cv_value_t;
114
    using pointer = _pointer_t;
115
    using reference = _reference_t;
116
    using iterator_category = _category_t;
117
};
118
/* ************************************************************************** */
119

120

121

122
/* *********** IMPLEMENTATION DETAILS: NARROWEST AND WIDEST TYPES *********** */
123
// Narrowest type structure declaration
124
template <class... T>
125
struct _narrowest_type;
126

127
// Narrowest type structure specialization: selects the only passed type
128
template <class T>
129
struct _narrowest_type<T>
130
: std::common_type<T>
131
{
132
    static_assert(binary_digits<T>::value, "");
133
};
134

135
// Narrowest type structure specialization: selects the type with less bits
136
template <class T, class U>
137
struct _narrowest_type<T, U>
138
: _narrowest_type<
139
    typename std::conditional<
140
        (binary_digits<T>::value < binary_digits<U>::value),
141
        T,
142
        typename std::conditional<
143
            (binary_digits<T>::value > binary_digits<U>::value),
144
            U,
145
            typename std::common_type<T, U>::type
146
        >::type
147
    >::type
148
>
149
{
150
};
151

152
// Narrowest type structure specialization: recursively selects the right type
153
template <class T, class... U>
154
struct _narrowest_type<T, U...>
155
: _narrowest_type<T, typename _narrowest_type<U...>::type>
156
{
157
};
158

159
// Narrowest type alias
160
template <class... T>
161
using _narrowest_type_t = typename _narrowest_type<T...>::type;
162

163
// Widest type structure declaration
164
template <class... X>
165
struct _widest_type;
166

167
// Widest type structure specialization: selects the only passed type
168
template <class T>
169
struct _widest_type<T>
170
: std::common_type<T>
171
{
172
    static_assert(binary_digits<T>::value, "");
173
};
174

175
// Widest type structure specialization: selects the type with more bits
176
template <class T, class U>
177
struct _widest_type<T, U>
178
: _widest_type<
179
    typename std::conditional<
180
        (binary_digits<T>::value > binary_digits<U>::value),
181
        T,
182
        typename std::conditional<
183
            (binary_digits<T>::value < binary_digits<U>::value),
184
            U,
185
            typename std::common_type<T, U>::type
186
        >::type
187
    >::type
188
>
189
{
190
};
191

192
// Widest type structure specialization: recursively selects the right type
193
template <class T, class... X>
194
struct _widest_type<T, X...>
195
: _widest_type<T, typename _widest_type<X...>::type>
196
{
197
};
198

199
// Widest type alias
200
template <class... T>
201
using _widest_type_t = typename _widest_type<T...>::type;
202
/* ************************************************************************** */
203

204

205

206
/* ************ IMPLEMENTATION DETAILS: NARROWER AND WIDER TYPES ************ */
207
// Narrower type structure definition
208
template <class T, int I = 0>
209
struct _narrower_type
210
{
211
    using tuple = std::tuple<
212
        unsigned long long int,
213
        unsigned long int,
214
        unsigned int,
215
        unsigned short int,
216
        unsigned char
217
    >;
218
    using lhs_bits = binary_digits<T>;
219
    using rhs_bits = binary_digits<typename std::tuple_element<I, tuple>::type>;
220
    using type = typename std::conditional<
221
        (lhs_bits::value > rhs_bits::value),
222
        typename std::tuple_element<I, tuple>::type,
223
        typename std::conditional<
224
            (I + 1 < std::tuple_size<tuple>::value),
225
            typename _narrower_type<
226
                T,
227
                (I + 1 < std::tuple_size<tuple>::value ? I + 1 : -1)
228
            >::type,
229
            typename std::tuple_element<I, tuple>::type
230
        >::type
231
    >::type;
232
};
233

234
// Narrower type structure specialization: not found
235
template <class T>
236
struct _narrower_type<T, -1>
237
{
238
    using type = T;
239
};
240

241
// Narrower type alias
242
template <class T>
243
using _narrower_type_t = typename _narrower_type<T>::type;
244

245
// Wider type structure definition
246
template <class T, int I = 0>
247
struct _wider_type
248
{
249
    using tuple = std::tuple<
250
        unsigned char,
251
        unsigned short int,
252
        unsigned int,
253
        unsigned long int,
254
        unsigned long long int
255
    >;
256
    using lhs_bits = binary_digits<T>;
257
    using rhs_bits = binary_digits<typename std::tuple_element<I, tuple>::type>;
258
    using type = typename std::conditional<
259
        (lhs_bits::value < rhs_bits::value),
260
        typename std::tuple_element<I, tuple>::type,
261
        typename std::conditional<
262
            (I + 1 < std::tuple_size<tuple>::value),
263
            typename _narrower_type<
264
                T,
265
                (I + 1 < std::tuple_size<tuple>::value ? I + 1 : -1)
266
            >::type,
267
            typename std::tuple_element<I, tuple>::type
268
        >::type
269
    >::type;
270
};
271

272
// Wider type structure specialization: not found
273
template <class T>
274
struct _wider_type<T, -1>
275
{
276
    using type = T;
277
};
278

279
// Wider type alias
280
template <class T>
281
using _wider_type_t = typename _wider_type<T>::type;
282
/* ************************************************************************** */
283

284

285

286
/* ******************* IMPLEMENTATION DETAILS: UTILITIES ******************** */
287
// Assertions
288
template <class Iterator>
289
constexpr bool _assert_range_viability(Iterator first, Iterator last);
290
/* ************************************************************************** */
291

292

293

294
/* ****************** IMPLEMENTATION DETAILS: INSTRUCTIONS ****************** */
295
// Population count
296
template <class T, class = decltype(__builtin_popcountll(T()))>
297
constexpr T _popcnt(T src) noexcept;
298
template <class T, class... X>
299
constexpr T _popcnt(T src, X...) noexcept;
300

301
// Leading zeros count
302
template <class T, class = decltype(__builtin_clzll(T()))>
303
constexpr T _lzcnt(T src) noexcept;
304
template <class T, class... X>
305
constexpr T _lzcnt(T src, X...) noexcept;
306

307
// Trailing zeros count
308
template <class T, class = decltype(__builtin_ctzll(T()))>
309
constexpr T _tzcnt(T src) noexcept;
310
template <class T, class... X>
311
constexpr T _tzcnt(T src, X...) noexcept;
312

313
// Bit field extraction
314
template <class T, class = decltype(__builtin_ia32_bextr_u64(T(), T(), T()))>
315
constexpr T _bextr(T src, T start, T len) noexcept;
316
template <class T, class... X>
317
constexpr T _bextr(T src, T start, T len, X...) noexcept;
318

319
// Parallel bits deposit
320
template <class T, class = decltype(_pdep_u64(T()))>
321
constexpr T _pdep(T src, T msk) noexcept;
322
template <class T, class... X>
323
constexpr T _pdep(T src, T msk, X...) noexcept;
324

325
// Parallel bits extract
326
template <class T, class = decltype(_pext_u64(T()))>
327
constexpr T _pext(T src, T msk) noexcept;
328
template <class T, class... X>
329
constexpr T _pext(T src, T msk, X...) noexcept;
330

331
// Byte swap
332
template <class T, class T128 = decltype(__uint128_t(__builtin_bswap64(T())))>
333
constexpr T _byteswap(T src) noexcept;
334
template <class T, class... X>
335
constexpr T _byteswap(T src, X...) noexcept;
336

337
// Bit swap
338
template <class T>
339
constexpr T _bitswap(T src) noexcept;
340
template <class T, std::size_t N>
341
constexpr T _bitswap(T src) noexcept;
342
template <class T, std::size_t N>
343
constexpr T _bitswap() noexcept;
344

345
// Bit blend
346
template <class T>
347
constexpr T _bitblend(T src0, T src1, T msk) noexcept;
348
template <class T>
349
constexpr T _bitblend(T src0, T src1, T start, T len) noexcept;
350

351
// Bit exchange
352
template <class T>
353
constexpr void _bitexch(T& src0, T& src1, T msk) noexcept;
354
template <class T, class S>
355
constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept;
356
template <class T, class S>
357
constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept;
358

359
// Bit compare
360
template <class T>
361
constexpr T _bitcmp(T src0, T src1, T start0, T start1, T len) noexcept;
362

363
// Double precision shift left
364
template <class T>
365
constexpr T _shld(T dst, T src, T cnt) noexcept;
366

367
// Double precision shift right
368
template <class T>
369
constexpr T _shrd(T dst, T src, T cnt) noexcept;
370

371
// Add carry
372
template <class... T>
373
using _supports_adc = decltype(__builtin_ia32_addcarryx_u64(T()...));
374
template <class C, class T, class = _supports_adc<C, T, T, std::nullptr_t>>
375
constexpr C _addcarry(C carry, T src0, T src1, T* dst) noexcept;
376
template <class C, class T, class... X>
377
constexpr C _addcarry(C carry, T src0, T src1, T* dst, X...) noexcept;
378

379
// Sub borrow
380
template <class... T>
381
using _supports_sbb = decltype(__builtin_ia32_sbb_u64(T()...));
382
template <class... T>
383
using _supports_sbb_alt = decltype(__builtin_ia32_subborrow_u64(T()...));
384
template <class B, class T, class = _supports_sbb<B, T, T, std::nullptr_t>>
385
constexpr B _subborrow(B borrow, T src0, T src1, T* dst) noexcept;
386
template <class B, class T, class = _supports_sbb_alt<B, T, T, std::nullptr_t>>
387
constexpr B _subborrow(const B& borrow, T src0, T src1, T* dst) noexcept;
388
template <class B, class T, class... X>
389
constexpr B _subborrow(B borrow, T src0, T src1, T* dst, X...) noexcept;
390

391
// Multiword multiply
392
template <class T, class T128 = decltype(__uint128_t(T()))>
393
constexpr T _mulx(T src0, T src1, T* hi) noexcept;
394
template <class T, class... X>
395
constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept;
396
/* ************************************************************************** */
397

398

399

400
// ------------- IMPLEMENTATION DETAILS: UTILITIES: ASSERTIONS -------------- //
401
// If the range allows multipass iteration, checks if last - first >= 0
402
template <class Iterator>
403
constexpr bool _assert_range_viability(Iterator first, Iterator last)
404
{
233,690✔
405
    using traits_t = std::iterator_traits<Iterator>;
233,690✔
406
    using category_t =  typename traits_t::iterator_category;
233,690✔
407
    using multi_t = std::forward_iterator_tag;
233,690✔
408
    constexpr bool is_multipass = std::is_base_of<multi_t, category_t>::value;
233,690✔
409
    const bool is_viable = !is_multipass || std::distance(first, last) >= 0;
233,690!
410
    assert(is_viable);
233,690!
411
    return is_viable;
233,690✔
412
}
233,690✔
413
// -------------------------------------------------------------------------- //
414

415

416

417
// --------- IMPLEMENTATION DETAILS: INSTRUCTIONS: POPULATION COUNT --------- //
418
// Counts the number of bits set to 1 with compiler intrinsics
419
template <class T, class>
420
constexpr T _popcnt(T src) noexcept
421
{
26,368✔
422
    static_assert(binary_digits<T>::value, "");
26,368✔
423
    constexpr T digits = binary_digits<T>::value;
26,368✔
424
    if (digits <= std::numeric_limits<unsigned int>::digits) {
26,368✔
425
        src = __builtin_popcount(src);
12,160✔
426
    } else if (digits <= std::numeric_limits<unsigned long int>::digits) {
14,208✔
427
        src = __builtin_popcountl(src);
14,208✔
428
    } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
14,208✔
UNCOV
429
        src = __builtin_popcountll(src);
×
UNCOV
430
    } else {
×
UNCOV
431
        src = _popcnt(src, std::ignore);
×
UNCOV
432
    }
×
433
    return src;
26,368✔
434
}
26,368✔
435

436
// Counts the number of bits set to 1 without compiler intrinsics
437
template <class T, class... X>
438
constexpr T _popcnt(T src, X...) noexcept
UNCOV
439
{
×
UNCOV
440
    static_assert(binary_digits<T>::value, "");
×
UNCOV
441
    T dst = T();
×
UNCOV
442
    for (dst = T(); src; src >>= 1) {
×
UNCOV
443
        dst += src & 1;
×
UNCOV
444
    }
×
UNCOV
445
    return dst;
×
UNCOV
446
}
×
447
// -------------------------------------------------------------------------- //
448

449

450

451
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: LEADING ZEROS COUNT -------- //
452
// Counts the number of leading zeros with compiler intrinsics
453
template <class T, class>
454
constexpr T _lzcnt(T src) noexcept
455
{
456
    static_assert(binary_digits<T>::value, "");
457
    constexpr T digits = binary_digits<T>::value;
458
    T dst = T();
459
    if (digits < std::numeric_limits<unsigned int>::digits) {
460
        dst = src ? __builtin_clz(src)
461
                     - (std::numeric_limits<unsigned int>::digits
462
                     - digits)
463
                   : digits;
464
    } else if (digits == std::numeric_limits<unsigned int>::digits) {
465
        dst = src ? __builtin_clz(src) : digits;
466
    } else if (digits < std::numeric_limits<unsigned long int>::digits) {
467
        dst = src ? __builtin_clzl(src)
468
                     - (std::numeric_limits<unsigned long int>::digits
469
                     - digits)
470
                   : digits;
471
    } else if (digits == std::numeric_limits<unsigned long int>::digits) {
472
        dst = src ? __builtin_clzl(src) : digits;
473
    } else if (digits < std::numeric_limits<unsigned long long int>::digits) {
474
        dst = src ? __builtin_clzll(src)
475
                     - (std::numeric_limits<unsigned long long int>::digits
476
                     - digits)
477
                   : digits;
478
    } else if (digits == std::numeric_limits<unsigned long long int>::digits) {
479
        dst = src ? __builtin_clzll(src) : digits;
480
    } else {
481
        dst = _lzcnt(src, std::ignore);
482
    }
483
    return dst;
484
}
485

486
// Counts the number of leading zeros without compiler intrinsics
487
template <class T, class... X>
488
constexpr T _lzcnt(T src, X...) noexcept
489
{
490
    static_assert(binary_digits<T>::value, "");
491
    constexpr T digits = binary_digits<T>::value;
492
    T dst = src != T();
493
    while (src >>= 1) {
494
        ++dst;
495
    }
496
    return digits - dst;
497
}
498
// -------------------------------------------------------------------------- //
499

500

501

502
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: TRAILING ZEROS COUNT ------- //
503
// Counts the number of trailing zeros with compiler intrinsics
504
template <class T, class>
505
constexpr T _tzcnt(T src) noexcept
506
{
16,622✔
507
    static_assert(binary_digits<T>::value, "");
16,622✔
508
    constexpr T digits = binary_digits<T>::value;
16,622✔
509
    T dst = T();
16,622✔
510
    if (digits <= std::numeric_limits<unsigned int>::digits) {
16,622✔
511
        dst = src ? __builtin_ctz(src) : digits;
8,110!
512
    } else if (digits <= std::numeric_limits<unsigned long int>::digits) {
8,512✔
513
        dst = src ? __builtin_ctzl(src) : digits;
8,512!
514
    } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
8,512✔
UNCOV
515
        dst = src ? __builtin_ctzll(src) : digits;
×
UNCOV
516
    } else {
×
UNCOV
517
        dst = _tzcnt(src, std::ignore);
×
UNCOV
518
    }
×
519
    return dst;
16,622✔
520
}
16,622✔
521

522
// Counts the number of trailing zeros without compiler intrinsics
523
template <class T, class... X>
524
constexpr T _tzcnt(T src, X...) noexcept
UNCOV
525
{
×
UNCOV
526
    static_assert(binary_digits<T>::value, "");
×
UNCOV
527
    constexpr T digits = binary_digits<T>::value;
×
UNCOV
528
    T dst = digits;
×
UNCOV
529
    if (src) {
×
UNCOV
530
        src = (src ^ (src - 1)) >> 1;
×
UNCOV
531
        for (dst = T(); src; dst++) {
×
UNCOV
532
            src >>= 1;
×
UNCOV
533
        }
×
UNCOV
534
    }
×
UNCOV
535
    return dst;
×
UNCOV
536
}
×
537
// -------------------------------------------------------------------------- //
538

539

540

541
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT FIELD EXTRACTION ------- //
542
// Extacts to lsbs a field of contiguous bits with compiler intrinsics
543
template <class T, class>
544
constexpr T _bextr(T src, T start, T len) noexcept
545
{
546
    static_assert(binary_digits<T>::value, "");
547
    constexpr T digits = binary_digits<T>::value;
548
    T dst = T();
549
    if (digits <= std::numeric_limits<unsigned int>::digits) {
550
        dst = __builtin_ia32_bextr_u32(src, start, len);
551
    } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
552
        dst = __builtin_ia32_bextr_u64(src, start, len);
553
    } else {
554
        dst = _bextr(src, start, len, std::ignore);
555
    }
556
    return dst;
557
}
558

559
// Extacts to lsbs a field of contiguous bits without compiler intrinsics
560
template <class T, class... X>
561
constexpr T _bextr(T src, T start, T len, X...) noexcept
562
{
3,712✔
563
    static_assert(binary_digits<T>::value, "");
3,712✔
564
    constexpr T digits = binary_digits<T>::value;
3,712✔
565
    constexpr T one = 1;
3,712✔
566
    const T msk = (one << len) * (len < digits) - one;
3,712✔
567
    return (src >> start) & msk * (start < digits);
3,712✔
568
}
3,712✔
569
// -------------------------------------------------------------------------- //
570

571

572

573
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: PARALLEL BIT DEPOSIT ------- //
574
// Deposits bits according to a mask with compiler instrinsics
575
template <class T, class>
576
constexpr T _pdep(T src, T msk) noexcept
577
{
578
    static_assert(binary_digits<T>::value, "");
579
    constexpr T digits = binary_digits<T>::value;
580
    T dst = T();
581
    if (digits <= std::numeric_limits<unsigned int>::digits) {
582
        dst = _pdep_u32(src, msk);
583
    } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
584
        dst = _pdep_u64(src, msk);
585
    } else {
586
        dst = _pdep(src, msk, std::ignore);
587
    }
588
    return dst;
589
}
590

591
// Deposits bits according to a mask without compiler instrinsics
592
template <class T, class... X>
593
constexpr T _pdep(T src, T msk, X...) noexcept
594
{
595
    static_assert(binary_digits<T>::value, "");
596
    constexpr T digits = binary_digits<T>::value;
597
    T dst = T();
598
    T cnt = T();
599
    while (msk) {
600
        dst >>= 1;
601
        if (msk & 1) {
602
            dst |= src << (digits - 1);
603
            src >>= 1;
604
        }
605
        msk >>= 1;
606
        ++cnt;
607
    }
608
    dst >>= (digits - cnt) * (cnt > 0);
609
    return dst;
610
}
611
// -------------------------------------------------------------------------- //
612

613

614

615
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: PARALLEL BIT EXTRACT ------- //
616
// Extracts bits according to a mask with compiler instrinsics
617
template <class T, class>
618
constexpr T _pext(T src, T msk) noexcept
619
{
620
    static_assert(binary_digits<T>::value, "");
621
    constexpr T digits = binary_digits<T>::value;
622
    T dst = T();
623
    if (digits <= std::numeric_limits<unsigned int>::digits) {
624
        dst = _pext_u32(src, msk);
625
    } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
626
        dst = _pext_u64(src, msk);
627
    } else {
628
        dst = _pext(src, msk, std::ignore);
629
    }
630
    return dst;
631
}
632

633
// Extracts bits according to a mask without compiler instrinsics
634
template <class T, class... X>
635
constexpr T _pext(T src, T msk, X...) noexcept
636
{
637
    static_assert(binary_digits<T>::value, "");
638
    constexpr T digits = binary_digits<T>::value;
639
    T dst = T();
640
    T cnt = T();
641
    while (msk) {
642
        if (msk & 1) {
643
            dst >>= 1;
644
            dst |= src << (digits - 1);
645
            ++cnt;
646
        }
647
        src >>= 1;
648
        msk >>= 1;
649
    }
650
    dst >>= (digits - cnt) * (cnt > 0);
651
    return dst;
652
}
653
// -------------------------------------------------------------------------- //
654

655

656

657
// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: BYTE SWAP ------------- //
658
// Reverses the order of the underlying bytes with compiler intrinsics
659
template <class T, class T128>
660
constexpr T _byteswap(T src) noexcept
661
{
662
    static_assert(binary_digits<T>::value, "");
663
    using byte_t = unsigned char;
664
    constexpr T digits = sizeof(T) * std::numeric_limits<byte_t>::digits;
665
    std::uint64_t tmp64 = 0;
666
    std::uint64_t* ptr64 = nullptr;
667
    if (std::is_same<T, T128>::value) {
668
        ptr64 = reinterpret_cast<std::uint64_t*>(&src);
669
        tmp64 = __builtin_bswap64(*ptr64);
670
        *ptr64 = __builtin_bswap64(*(ptr64 + 1));
671
        *(ptr64 + 1) = tmp64;
672
    } else if (digits == std::numeric_limits<std::uint16_t>::digits) {
673
        src = __builtin_bswap16(src);
674
    } else if (digits == std::numeric_limits<std::uint32_t>::digits) {
675
        src = __builtin_bswap32(src);
676
    } else if (digits == std::numeric_limits<std::uint64_t>::digits)  {
677
        src = __builtin_bswap64(src);
678
    } else if (digits > std::numeric_limits<byte_t>::digits) {
679
        src = _byteswap(src, std::ignore);
680
    }
681
    return src;
682
}
683

684
// Reverses the order of the underlying bytes without compiler intrinsics
685
template <class T, class... X>
686
constexpr T _byteswap(T src, X...) noexcept
687
{
688
    static_assert(binary_digits<T>::value, "");
689
    using byte_t = unsigned char;
690
    constexpr T half = sizeof(T) / 2;
691
    constexpr T end = sizeof(T) - 1;
692
    unsigned char* bytes = reinterpret_cast<byte_t*>(&src);
693
    unsigned char byte = 0;
694
    for (T i = T(); i < half; ++i) {
695
        byte = bytes[i];
696
        bytes[i] = bytes[end - i];
697
        bytes[end - i] = byte;
698
    }
699
    return src;
700
}
701
// -------------------------------------------------------------------------- //
702

703

704

705
// ------------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT SWAP ------------- //
706
// Reverses the order of the bits with or without of compiler intrinsics
707
template <class T>
708
constexpr T _bitswap(T src) noexcept
709
{
5,260,704✔
710
    static_assert(binary_digits<T>::value, "");
5,260,704✔
711
    using byte_t = unsigned char;
5,260,704✔
712
    constexpr auto ignore = nullptr;
5,260,704✔
713
    constexpr T digits = binary_digits<T>::value;
5,260,704✔
714
    constexpr unsigned long long int first = 0x80200802ULL;
5,260,704✔
715
    constexpr unsigned long long int second = 0x0884422110ULL;
5,260,704✔
716
    constexpr unsigned long long int third = 0x0101010101ULL;
5,260,704✔
717
    constexpr unsigned long long int fourth = 32;
5,260,704✔
718
    constexpr bool is_size1 = sizeof(T) == 1;
5,260,704✔
719
    constexpr bool is_byte = digits == std::numeric_limits<byte_t>::digits;
5,260,704✔
720
    constexpr bool is_octet = std::numeric_limits<byte_t>::digits == 8;
5,260,704✔
721
    constexpr bool is_pow2 = _popcnt(digits, ignore) == 1;
5,260,704✔
722
    T dst = src;
5,260,704✔
723
    T i = digits - 1;
5,260,704✔
724
    if (is_size1 && is_byte && is_octet) {
5,260,704✔
725
        dst = ((src * first) & second) * third >> fourth;
1,311,056✔
726
    } else if (is_pow2) {
3,949,648✔
727
        dst = _bitswap<T, digits>(src);
3,949,648✔
728
    } else {
3,949,648✔
UNCOV
729
        for (src >>= 1; src; src >>= 1) {
×
UNCOV
730
            dst <<= 1;
×
UNCOV
731
            dst |= src & 1;
×
UNCOV
732
            i--;
×
UNCOV
733
        }
×
UNCOV
734
        dst <<= i;
×
UNCOV
735
    }
×
736
    return dst;
5,260,704✔
737
}
5,260,704✔
738

739
// Reverses the order of the bits: recursive metafunction
740
template <class T, std::size_t N>
741
constexpr T _bitswap(T src) noexcept
742
{
19,755,920✔
743
    static_assert(binary_digits<T>::value, "");
19,755,920✔
744
    constexpr T cnt = N >> 1;
19,755,920✔
745
    constexpr T msk = _bitswap<T, cnt>();
19,755,920✔
746
    src = ((src >> cnt) & msk) | ((src << cnt) & ~msk);
19,755,920✔
747
    return cnt > 1 ? _bitswap<T, cnt>(src) : src;
19,755,920✔
748
}
19,755,920✔
749

750
// Reverses the order of the bits: mask for the recursive metafunction
751
template <class T, std::size_t N>
752
constexpr T _bitswap() noexcept
UNCOV
753
{
×
UNCOV
754
    static_assert(binary_digits<T>::value, "");
×
UNCOV
755
    constexpr T digits = binary_digits<T>::value;
×
UNCOV
756
    T cnt = digits;
×
UNCOV
757
    T msk = ~T();
×
UNCOV
758
    while (cnt != N) {
×
UNCOV
759
        cnt >>= 1;
×
UNCOV
760
        msk ^= (msk << cnt);
×
UNCOV
761
    }
×
UNCOV
762
    return msk;
×
UNCOV
763
}
×
764
// -------------------------------------------------------------------------- //
765

766

767

768
// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT BLEND ------------- //
769
// Replaces bits of src0 by the ones of src1 where the mask is true
770
template <class T>
771
constexpr T _bitblend(T src0, T src1, T msk) noexcept
772
{
2,495✔
773
    static_assert(binary_digits<T>::value, "");
2,495✔
774
    return src0 ^ ((src0 ^ src1) & msk);
2,495✔
775
}
2,495✔
776

777
// Replaces len bits of src0 by the ones of src1 starting at start
778
template <class T>
779
constexpr T _bitblend(T src0, T src1, T start, T len) noexcept
780
{
494,372✔
781
    static_assert(binary_digits<T>::value, "");
494,372✔
782
    constexpr T digits = binary_digits<T>::value;
494,372✔
783
    constexpr T one = 1;
494,372✔
784
    // The digits_mask is solely here to prevent Undefined Sanitizer
785
    // complaining about shift of len >= digits
786
    // Note: on -O1 the (len & digits_mask) is optimized to simply (len)
787
    constexpr T digits_mask = digits - one;
494,372✔
788
    const T msk = ((one << (len & digits_mask)) * (len < digits) - one) << start;
494,372✔
789
    return src0 ^ ((src0 ^ src1) & msk * (start < digits));
494,372✔
790
}
494,372✔
791
// -------------------------------------------------------------------------- //
792

793

794

795
// ---------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT EXCHANGE ------------ //
796
// Exchanges/swaps bits of src0 by the ones of src1 where the mask is true
797
template <class T>
798
constexpr void _bitexch(T& src0, T& src1, T msk) noexcept
799
{
800
    src0 = src0 ^ static_cast<T>(src1 & msk);
801
    src1 = src1 ^ static_cast<T>(src0 & msk);
802
    src0 = src0 ^ static_cast<T>(src1 & msk);
803
    return;
804
}
805

806
// Replaces len bits of src0 by the ones of src1 starting at start
807
template <class T, class S>
808
constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept
809
{
1,391✔
810
    static_assert(binary_digits<T>::value, "");
1,391✔
811
    constexpr auto digits = binary_digits<T>::value;
1,391✔
812
    constexpr T one = 1;
1,391✔
813
    const T msk = (len < digits)
1,391!
814
        ? ((one << len) - one) << start : -1;
1,391✔
815
    src0 = src0 ^ static_cast<T>(src1 & msk);
1,391✔
816
    src1 = src1 ^ static_cast<T>(src0 & msk);
1,391✔
817
    src0 = src0 ^ static_cast<T>(src1 & msk);
1,391✔
818
    return;
1,391✔
819
}
1,391✔
820

821
// Replaces len bits of src0 by the ones of src1 starting at start0
822
// in src0 and start1 in src1.
823
// len <= digits-max(start0, start1)
824
template <class T, class S>
825
constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept
826
{
10,244,208✔
827
    static_assert(binary_digits<T>::value, "");
10,244,208✔
828
    constexpr auto digits = binary_digits<T>::value;
10,244,208✔
829
    constexpr T one = 1;
10,244,208✔
830
    const T msk = (len < digits) ?
10,244,208!
831
        ((one << len) - one) : -1;
10,244,208✔
832
    if (start0 >= start1) {
10,244,208✔
833
        src0 = src0 ^ (
5,123,232✔
834
                static_cast<T>(src1 << (start0 - start1))
5,123,232✔
835
                &
5,123,232✔
836
                static_cast<T>(msk << start0)
5,123,232✔
837
        );
5,123,232✔
838
        src1 = src1 ^ (
5,123,232✔
839
                static_cast<T>(src0 >> (start0 - start1))
5,123,232✔
840
                &
5,123,232✔
841
                static_cast<T>(msk << start1)
5,123,232✔
842
        );
5,123,232✔
843
        src0 = src0 ^ (
5,123,232✔
844
                static_cast<T>(src1 << (start0 - start1))
5,123,232✔
845
                &
5,123,232✔
846
                static_cast<T>(msk << start0)
5,123,232✔
847
        );
5,123,232✔
848
    } else {
5,123,232✔
849
        src0 = src0 ^ (
5,120,976✔
850
                static_cast<T>(src1 >> (start1 - start0))
5,120,976✔
851
                &
5,120,976✔
852
                static_cast<T>(msk << start0)
5,120,976✔
853
        );
5,120,976✔
854
        src1 = src1 ^ (
5,120,976✔
855
                static_cast<T>(src0 << (start1 - start0))
5,120,976✔
856
                &
5,120,976✔
857
                static_cast<T>(msk << start1)
5,120,976✔
858
        );
5,120,976✔
859
        src0 = src0 ^ (
5,120,976✔
860
                static_cast<T>(src1 >> (start1 - start0))
5,120,976✔
861
                &
5,120,976✔
862
                static_cast<T>(msk << start0)
5,120,976✔
863
        );
5,120,976✔
864
    }
5,120,976✔
865
    return;
10,244,208✔
866
}
10,244,208✔
867
// -------------------------------------------------------------------------- //
868

869

870

871
// ----------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT COMPARE ------------ //
872
// Compares a subsequence of bits within src0 and src1 and returns 0 if equal
873
template <class T>
874
constexpr T _bitcmp(T src0, T src1, T start0, T start1, T len) noexcept
875
{
876
    static_assert(binary_digits<T>::value, "");
877
    return _bextr(src0, start0, len) == _bextr(src1, start1, len);
878
}
879
// -------------------------------------------------------------------------- //
880

881

882

883
// --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT LEFT ---- //
884
// Left shifts dst by cnt bits, filling the lsbs of dst by the msbs of src
885
template <class T>
886
constexpr T _shld(T dst, T src, T cnt) noexcept
887
{
888
    static_assert(binary_digits<T>::value, "");
889
    constexpr T digits = binary_digits<T>::value;
890
    if (cnt < digits) {
891
        dst = (dst << cnt) | (src >> (digits - cnt));
892
    } else {
893
        dst = (src << (cnt - digits)) * (cnt < digits + digits);
894
    }
895
    return dst;
896
}
897
// -------------------------------------------------------------------------- //
898

899

900

901
// --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT RIGHT --- //
902
// Right shifts dst by cnt bits, filling the msbs of dst by the lsbs of src
903
template <class T>
904
constexpr T _shrd(T dst, T src, T cnt) noexcept
905
{
6,257,882✔
906
    static_assert(binary_digits<T>::value, "");
6,257,882✔
907
    constexpr T digits = binary_digits<T>::value;
6,257,882✔
908
    if (cnt < digits) {
6,257,882!
909
        dst = (dst >> cnt) | (src << (digits - cnt));
6,257,882✔
910
    } else {
6,257,882✔
911
        dst = (src >> (cnt - digits)) * (cnt < digits + digits);
×
UNCOV
912
    }
×
913
    return dst;
6,257,882✔
914
}
6,257,882✔
915
// -------------------------------------------------------------------------- //
916

917

918

919
// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: ADD CARRY ------------- //
920
// Adds src0 and src1 and returns the new carry bit with intrinsics
921
template <class C, class T, class>
922
constexpr C _addcarry(C carry, T src0, T src1, T* dst) noexcept
923
{
924
    static_assert(binary_digits<T>::value, "");
925
    using wider_t = typename _wider_type<T>::type;
926
    constexpr T digits = binary_digits<T>::value;
927
    wider_t tmp = 0;
928
    unsigned int udst = 0;
929
    unsigned long long int ulldst = 0;
930
    if (digits == std::numeric_limits<unsigned int>::digits) {
931
        carry = __builtin_ia32_addcarryx_u32(carry, src0, src1, &udst);
932
        *dst = udst;
933
    } else if (digits == std::numeric_limits<unsigned long long int>::digits) {
934
        carry = __builtin_ia32_addcarryx_u64(carry, src0, src1, &ulldst);
935
        *dst = ulldst;
936
    } else if (digits < binary_digits<wider_t>::value) {
937
        tmp = static_cast<wider_t>(src0) + static_cast<wider_t>(src1);
938
        tmp += static_cast<wider_t>(static_cast<bool>(carry));
939
        *dst = tmp;
940
        carry = static_cast<bool>(tmp >> digits);
941
    } else {
942
        carry = _addcarry(carry, src0, src1, dst, std::ignore);
943
    }
944
    return carry;
945
}
946

947
// Adds src0 and src1 and returns the new carry bit without intrinsics
948
template <class C, class T, class... X>
949
constexpr C _addcarry(C carry, T src0, T src1, T* dst, X...) noexcept
950
{
951
    static_assert(binary_digits<T>::value, "");
952
    *dst = src0 + src1 + static_cast<T>(static_cast<bool>(carry));
953
    return carry ? *dst <= src0 || *dst <= src1 : *dst < src0 || *dst < src1;
954
}
955
// -------------------------------------------------------------------------- //
956

957

958

959
// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: SUB BORROW ------------ //
960
// Subtracts src1 to src0 and returns the new borrow bit with intrinsics
961
template <class B, class T, class>
962
constexpr B _subborrow(B borrow, T src0, T src1, T* dst) noexcept
963
{
964
    static_assert(binary_digits<T>::value, "");
965
    using wider_t = typename _wider_type<T>::type;
966
    constexpr T digits = binary_digits<T>::value;
967
    wider_t tmp = 0;
968
    unsigned int udst = 0;
969
    unsigned long long int ulldst = 0;
970
    if (digits == std::numeric_limits<unsigned int>::digits) {
971
        borrow = __builtin_ia32_sbb_u32(borrow, src0, src1, &udst);
972
        *dst = udst;
973
    } else if (digits == std::numeric_limits<unsigned long long int>::digits) {
974
        borrow = __builtin_ia32_sbb_u64(borrow, src0, src1, &ulldst);
975
        *dst = ulldst;
976
    } else if (digits < binary_digits<wider_t>::value) {
977
        tmp = static_cast<wider_t>(src1);
978
        tmp += static_cast<wider_t>(static_cast<bool>(borrow));
979
        borrow = tmp > static_cast<wider_t>(src0);
980
        *dst = static_cast<wider_t>(src0) - tmp;
981
    } else {
982
        borrow = _subborrow(borrow, src0, src1, dst, std::ignore);
983
    }
984
    return borrow;
985
}
986

987
// Subtracts src1 to src0 and returns the new borrow bit with other intrinsics
988
template <class B, class T, class>
989
constexpr B _subborrow(const B& borrow, T src0, T src1, T* dst) noexcept
990
{
991
    static_assert(binary_digits<T>::value, "");
992
    using wider_t = typename _wider_type<T>::type;
993
    constexpr T digits = binary_digits<T>::value;
994
    wider_t tmp = 0;
995
    unsigned int udst = 0;
996
    unsigned long long int ulldst = 0;
997
    B flag = borrow;
998
    if (digits == std::numeric_limits<unsigned int>::digits) {
999
        flag = __builtin_ia32_subborrow_u32(borrow, src0, src1, &udst);
1000
        *dst = udst;
1001
    } else if (digits == std::numeric_limits<unsigned long long int>::digits) {
1002
        flag = __builtin_ia32_subborrow_u64(borrow, src0, src1, &ulldst);
1003
        *dst = ulldst;
1004
    } else if (digits < binary_digits<wider_t>::value) {
1005
        tmp = static_cast<wider_t>(src1);
1006
        tmp += static_cast<wider_t>(static_cast<bool>(borrow));
1007
        flag = tmp > static_cast<wider_t>(src0);
1008
        *dst = static_cast<wider_t>(src0) - tmp;
1009
    } else {
1010
        flag = _subborrow(borrow, src0, src1, dst, std::ignore);
1011
    }
1012
    return flag;
1013
}
1014

1015
// Subtracts src1 to src0 and returns the new borrow bit without intrinsics
1016
template <class B, class T, class... X>
1017
constexpr B _subborrow(B borrow, T src0, T src1, T* dst, X...) noexcept
1018
{
1019
    static_assert(binary_digits<T>::value, "");
1020
    *dst = src0 - (src1 + static_cast<T>(static_cast<bool>(borrow)));
1021
    return borrow ? src1 >= src0 : src1 > src0;
1022
}
1023
// -------------------------------------------------------------------------- //
1024

1025

1026

1027
// -------- IMPLEMENTATION DETAILS: INSTRUCTIONS: MULTIWORD MULTIPLY -------- //
1028
// Multiplies src0 and src1 and gets the full result with compiler intrinsics
1029
template <class T, class T128>
1030
constexpr T _mulx(T src0, T src1, T* hi) noexcept
1031
{
1032
    static_assert(binary_digits<T>::value, "");
1033
    using wider_t = typename _wider_type<T>::type;
1034
    constexpr T digits = binary_digits<T>::value;
1035
    wider_t tmp = 0;
1036
    T128 tmp128 = 0;
1037
    T lo = 0;
1038
    if (digits == std::numeric_limits<std::uint64_t>::digits) {
1039
        tmp128 = static_cast<T128>(src0) * static_cast<T128>(src1);
1040
        *hi = tmp128 >> digits;
1041
        lo = tmp128;
1042
    } else if (digits + digits == binary_digits<wider_t>::value) {
1043
        tmp = static_cast<wider_t>(src0) * static_cast<wider_t>(src1);
1044
        *hi = tmp >> digits;
1045
        lo = tmp;
1046
    } else {
1047
        lo = _mulx(src0, src1, hi, std::ignore);
1048
    }
1049
    return lo;
1050
}
1051

1052
// Multiplies src0 and src1 and gets the full result without compiler intrinsics
1053
template <class T, class... X>
1054
constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept
1055
{
1056
    static_assert(binary_digits<T>::value, "");
1057
    constexpr T digits = binary_digits<T>::value;
1058
    constexpr T offset = digits / 2;
1059
    constexpr T ones = ~static_cast<T>(0);
1060
    const T lsbs0 = src0 & static_cast<T>(ones >> (digits - offset));
1061
    const T msbs0 = src0 >> offset;
1062
    const T lsbs1 = src1 & static_cast<T>(ones >> (digits - offset));
1063
    const T msbs1 = src1 >> offset;
1064
    const T llsbs = lsbs0 * lsbs1;
1065
    const T mlsbs = msbs0 * lsbs1;
1066
    const T lmsbs = lsbs0 * msbs1;
1067
    const T mi = mlsbs + lmsbs;
1068
    const T lo = llsbs + static_cast<T>(mi << offset);
1069
    const T lcarry = lo < llsbs || lo < static_cast<T>(mi << offset);
1070
    const T mcarry = static_cast<T>(mi < mlsbs || mi < lmsbs) << offset;
1071
    *hi = static_cast<T>(mi >> offset) + msbs0 * msbs1 + mcarry + lcarry;
1072
    return lo;
1073
}
1074
// -------------------------------------------------------------------------- //
1075

1076

1077

1078
// ========================================================================== //
1079
} // namespace bit
1080
#endif // _BIT_DETAILS_HPP_INCLUDED
1081
// ========================================================================== //
1082

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc