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

PeterCDMcLean / BitLib / 14949319596

10 May 2025 09:32PM UTC coverage: 96.898% (-0.1%) from 97.036%
14949319596

Pull #8

github

web-flow
Merge 153e605bc into a837637fd
Pull Request #8: Common bit array base

171 of 179 new or added lines in 5 files covered. (95.53%)

1 existing line in 1 file now uncovered.

1312 of 1354 relevant lines covered (96.9%)

16164538.6 hits per line

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

98.96
/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
template <typename T>
72
using smallest_integral = std::conditional_t<
73
    (sizeof(T) <= sizeof(std::uint8_t)),
74
    std::uint8_t,
75
    std::conditional_t<
76
        (sizeof(T) <= sizeof(std::uint16_t)),
77
        std::uint16_t,
78
        std::conditional_t<
79
            (sizeof(T) <= sizeof(std::uint32_t)),
80
            std::uint32_t,
81
            std::conditional_t<
82
                (sizeof(T) <= sizeof(std::uint64_t)),
83
                std::uint64_t,
84
                T>>>>;
85

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

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

118

119

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

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

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

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

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

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

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

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

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

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

202

203

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

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

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

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

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

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

282

283

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

290

291

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

396

397

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

413

414

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

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

447

448

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

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

498

499

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

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

537

538

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

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

569

570

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

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

611

612

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

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

653

654

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

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

701

702

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

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

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

764

765

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

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

791

792

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

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

819
// Replaces len bits of src0 by the ones of src1 starting at start0
820
// in src0 and start1 in src1.
821
// len <= digits-max(start0, start1)
822
template <class T, class S>
823
constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept
9,808,148✔
824
{
825
    static_assert(binary_digits<T>::value, "");
826
    constexpr auto digits = binary_digits<T>::value;
9,808,148✔
827
    constexpr T one = 1;
9,808,148✔
828
    const T msk = (len < digits) ?
9,808,148✔
829
        ((one << len) - one) : -1;
9,808,148✔
830
    if (start0 >= start1) {
9,808,148✔
831
        src0 = src0 ^ (
9,808,136✔
832
                static_cast<T>(src1 << (start0 - start1))
4,904,068✔
833
                &
4,904,068✔
834
                static_cast<T>(msk << start0)
9,808,136✔
835
        );
836
        src1 = src1 ^ (
9,808,136✔
837
                static_cast<T>(src0 >> (start0 - start1))
4,904,068✔
838
                &
4,904,068✔
839
                static_cast<T>(msk << start1)
9,808,136✔
840
        );
841
        src0 = src0 ^ (
14,712,204✔
842
                static_cast<T>(src1 << (start0 - start1))
4,904,068✔
843
                &
4,904,068✔
844
                static_cast<T>(msk << start0)
9,808,136✔
845
        );
846
    } else {
847
        src0 = src0 ^ (
9,808,160✔
848
                static_cast<T>(src1 >> (start1 - start0))
4,904,080✔
849
                &
4,904,080✔
850
                static_cast<T>(msk << start0)
9,808,160✔
851
        );
852
        src1 = src1 ^ (
9,808,160✔
853
                static_cast<T>(src0 << (start1 - start0))
4,904,080✔
854
                &
4,904,080✔
855
                static_cast<T>(msk << start1)
9,808,160✔
856
        );
857
        src0 = src0 ^ (
14,712,240✔
858
                static_cast<T>(src1 >> (start1 - start0))
4,904,080✔
859
                &
4,904,080✔
860
                static_cast<T>(msk << start0)
9,808,160✔
861
        );
862
    }
863
    return;
9,808,148✔
864
}
865
// -------------------------------------------------------------------------- //
866

867

868

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

879

880

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

897

898

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

915

916

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

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

955

956

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

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

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

1023

1024

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

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

1074

1075

1076
// ========================================================================== //
1077
} // namespace bit
1078
#endif // _BIT_DETAILS_HPP_INCLUDED
1079
// ========================================================================== //
1080

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