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

PeterCDMcLean / BitLib / 15696998807

17 Jun 2025 02:58AM UTC coverage: 54.11% (+0.6%) from 53.519%
15696998807

push

github

web-flow
Truncation policy (#17)

* Add draft policies for integer conversion

* Add allocator to Policy. Use policy to construct from integral

* Add convenience conversion pathway for literal -> bit_value

* Add algorithm accumulate and utility to_string

* template keyword only necessary if actually passing template args

* Better UX for accumulate policy

* small tweaks to to_string

* Add static_assert error to bit_literal.

* Consolidate from_integral code

* Fix accumulate, get_word doesn't mask

10137 of 18710 branches covered (54.18%)

Branch coverage included in aggregate %.

212 of 251 new or added lines in 13 files covered. (84.46%)

214 existing lines in 11 files now uncovered.

6062 of 11227 relevant lines covered (53.99%)

7736453.1 hits per line

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

68.71
/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 <immintrin.h>
19

20
#include <algorithm>
21
#include <cassert>
22
#include <concepts>
23
#include <cstddef>
24
#include <cstdint>
25
#include <iterator>
26
#include <limits>
27
#include <ranges>
28
#include <stdexcept>
29
#include <tuple>
30
#include <type_traits>
31
#include <utility>
32

33
#include "bitlib/bit-containers/bit_bitsof.hpp"
34
#include "bitlib/bit_concepts.hpp"
35

36
// Project sources
37
// Third-party libraries
38
// Miscellaneous
39
namespace bit {
40
class bit_value;
41
template <class WordType>
42
class bit_reference;
43
template <class Iterator>
44
class bit_iterator;
45
template <class WordType>
46
using bit_pointer = bit_iterator<WordType*>;
47
template <typename target_word_ptr, typename source_word_ptr>
48
class bit_word_pointer_adapter;
49

50
// ========================================================================== //
51

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

63
// Specialization for std::byte.
64
template <>
65
struct binary_digits_impl<std::byte, true> : std::integral_constant<std::size_t, std::numeric_limits<unsigned char>::digits> {};
66

67
// Public interface that removes cv-qualifiers.
68
template <typename UIntType>
69
struct binary_digits : binary_digits_impl<std::remove_cv_t<UIntType>> {};
70

71
// Binary digits value
72
template <class T>
73
constexpr std::size_t binary_digits_v = binary_digits<T>::value;
74
/*************************************************************************** */
75

76
template <size_t N>
77
using ceil_integral = std::conditional_t<
78
    (N <= bitsof<std::uint8_t>()),
79
    std::uint8_t,
80
    std::conditional_t<
81
        (N <= bitsof<std::uint16_t>()),
82
        std::uint16_t,
83
        std::conditional_t<
84
            (N <= bitsof<std::uint32_t>()),
85
            std::uint32_t,
86
            std::conditional_t<
87
                (N <= bitsof<std::uint64_t>()),
88
                std::uint64_t,
89
                std::uint64_t>>>>;
90

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

111
    //    static_assert(std::is_same<_raw_pointer_t, _raw_value_t>::value, "");
112
    //    static_assert(std::is_same<_raw_reference_t, _raw_value_t>::value, "");
113

114
    // Types
115
    public:
116
    using difference_type = _difference_t;
117
    using value_type = _cv_value_t;
118
    using pointer = _pointer_t;
119
    using reference = _reference_t;
120
    using iterator_category = _category_t;
121
};
122
/* ************************************************************************** */
123

124
#if 0
125
/* *********** IMPLEMENTATION DETAILS: NARROWEST AND WIDEST TYPES *********** */
126
// Narrowest type structure declaration
127
template <class... T>
128
struct _narrowest_type;
129

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

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

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

162
// Narrowest type alias
163
template <class... T>
164
using _narrowest_type_t = typename _narrowest_type<T...>::type;
165

166
// Widest type structure declaration
167
template <class... X>
168
struct _widest_type;
169

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

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

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

202
// Widest type alias
203
template <class... T>
204
using _widest_type_t = typename _widest_type<T...>::type;
205
/* ************************************************************************** */
206

207

208

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

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

244
// Narrower type alias
245
template <class T>
246
using _narrower_type_t = typename _narrower_type<T>::type;
247

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

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

282
// Wider type alias
283
template <class T>
284
using _wider_type_t = typename _wider_type<T>::type;
285
/* ************************************************************************** */
286
#endif
287

288
/*
289
exact_ceil_integral is used to determine the exact integral type that a proxy reference
290
can be implicitly converted to.
291
If the proxy reference supports multiple types, it will pick the largest, preferring unsigned.
292
*/
293
template <typename T>
294
struct exact_ceil_integral {
295
 private:
296
  using U = std::remove_cvref_t<T>;
297

298
  template <typename From, typename To>
UNCOV
299
  static constexpr bool is_exactly_convertible() {
UNCOV
300
    if constexpr (!std::is_convertible_v<From, To>) {
UNCOV
301
      return false;
UNCOV
302
    } else {
UNCOV
303
      // Try brace-initialization to detect narrowing
UNCOV
304
      return requires(From f) {
UNCOV
305
        To{f};  // will fail if narrowing
UNCOV
306
      };
UNCOV
307
    }
UNCOV
308
  }
309

310
 public:
311
  using type = std::conditional_t<
312
      is_exactly_convertible<U, uint64_t>(), uint64_t,
313
      std::conditional_t<
314
          is_exactly_convertible<U, int64_t>(), int64_t,
315
          std::conditional_t<
316
              is_exactly_convertible<U, uint32_t>(), uint32_t,
317
              std::conditional_t<
318
                  is_exactly_convertible<U, int32_t>(), int32_t,
319
                  std::conditional_t<
320
                      is_exactly_convertible<U, uint16_t>(), uint16_t,
321
                      std::conditional_t<
322
                          is_exactly_convertible<U, int16_t>(), int16_t,
323
                          std::conditional_t<
324
                              is_exactly_convertible<U, uint8_t>(), uint8_t,
325
                              std::conditional_t<
326
                                  is_exactly_convertible<U, int8_t>(), int8_t,
327
                                  void>>>>>>>>;
328
};
329

330
// Helper alias
331
template <typename T>
332
using exact_ceil_integral_t = typename exact_ceil_integral<T>::type;
333

334
/* ******************* IMPLEMENTATION DETAILS: UTILITIES ******************** */
335
// Assertions
336
template <class Iterator>
337
constexpr bool _assert_range_viability(Iterator first, Iterator last);
338
/* ************************************************************************** */
339

340

341

342
/* ****************** IMPLEMENTATION DETAILS: INSTRUCTIONS ****************** */
343
// Population count
344
template <class T, class = decltype(__builtin_popcountll(T()))>
345
constexpr T _popcnt(T src) noexcept;
346
template <class T, class... X>
347
constexpr T _popcnt(T src, X...) noexcept;
348

349
// Leading zeros count
350
template <class T, class = decltype(__builtin_clzll(T()))>
351
constexpr T _lzcnt(T src) noexcept;
352
template <class T, class... X>
353
constexpr T _lzcnt(T src, X...) noexcept;
354

355
// Trailing zeros count
356
template <class T, class = decltype(__builtin_ctzll(T()))>
357
constexpr T _tzcnt(T src) noexcept;
358
template <class T, class... X>
359
constexpr T _tzcnt(T src, X...) noexcept;
360

361
// Bit field extraction
362
template <class T, class = decltype(__builtin_ia32_bextr_u64(T(), T(), T()))>
363
constexpr T _bextr(T src, T start, T len) noexcept;
364
template <class T, class... X>
365
constexpr T _bextr(T src, T start, T len, X...) noexcept;
366

367
#if 0
368
// Parallel bits deposit
369
template <class T, class = decltype(_pdep_u64(T()))>
370
constexpr T _pdep(T src, T msk) noexcept;
371
template <class T, class... X>
372
constexpr T _pdep(T src, T msk, X...) noexcept;
373

374
// Parallel bits extract
375
template <class T, class = decltype(_pext_u64(T()))>
376
constexpr T _pext(T src, T msk) noexcept;
377
template <class T, class... X>
378
constexpr T _pext(T src, T msk, X...) noexcept;
379

380
// Byte swap
381
template <class T, class T128 = decltype(__uint128_t(__builtin_bswap64(T())))>
382
constexpr T _byteswap(T src) noexcept;
383
template <class T, class... X>
384
constexpr T _byteswap(T src, X...) noexcept;
385

386
#endif
387

388
// Bit swap
389
template <class T>
390
constexpr T _bitswap(T src) noexcept;
391
template <class T, std::size_t N>
392
constexpr T _bitswap(T src) noexcept;
393
template <class T, std::size_t N>
394
constexpr T _bitswap() noexcept;
395

396
// Bit exchange
397
template <class T>
398
constexpr void _bitexch(T& src0, T& src1, T msk) noexcept;
399
template <class T, class S>
400
constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept;
401
template <class T, class S>
402
constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept;
403

404
// Bit compare
405
template <class T>
406
constexpr T _bitcmp(T src0, T src1, T start0, T start1, T len) noexcept;
407

408
// Double precision shift left
409
template <class T>
410
constexpr T _shld(T dst, T src, T cnt) noexcept;
411

412
// Double precision shift right
413
template <class T>
414
constexpr T _shrd(T dst, T src, T cnt) noexcept;
415

416
// Add carry
417
template <class... T>
418
using _supports_adc = decltype(__builtin_ia32_addcarryx_u64(T()...));
419
template <class C, class T, class = _supports_adc<C, T, T, std::nullptr_t>>
420
constexpr C _addcarry(C carry, T src0, T src1, T* dst) noexcept;
421
template <class C, class T, class... X>
422
constexpr C _addcarry(C carry, T src0, T src1, T* dst, X...) noexcept;
423

424
// Sub borrow
425
template <class... T>
426
using _supports_sbb = decltype(__builtin_ia32_sbb_u64(T()...));
427
template <class... T>
428
using _supports_sbb_alt = decltype(__builtin_ia32_subborrow_u64(T()...));
429
template <class B, class T, class = _supports_sbb<B, T, T, std::nullptr_t>>
430
constexpr B _subborrow(B borrow, T src0, T src1, T* dst) noexcept;
431
template <class B, class T, class = _supports_sbb_alt<B, T, T, std::nullptr_t>>
432
constexpr B _subborrow(const B& borrow, T src0, T src1, T* dst) noexcept;
433
template <class B, class T, class... X>
434
constexpr B _subborrow(B borrow, T src0, T src1, T* dst, X...) noexcept;
435

436
// Multiword multiply
437
template <class T, class T128 = decltype(__uint128_t(T()))>
438
constexpr T _mulx(T src0, T src1, T* hi) noexcept;
439
template <class T, class... X>
440
constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept;
441
/* ************************************************************************** */
442

443
/*
444
Logical shift right
445
*/
446
template <std::integral T, typename size_type = size_t>
447
constexpr T lsr(const T val, const size_type shift) {
172,271,195✔
448
  return static_cast<T>(static_cast<std::make_unsigned_t<T>>(val) >> shift);
172,271,195✔
449
}
86,070,278✔
450

451
template <typename T, typename size_type = size_t>
452
constexpr exact_ceil_integral_t<T> lsr(const T val, const size_type shift) {
12,748✔
453
  return static_cast<exact_ceil_integral_t<T>>(static_cast<std::make_unsigned_t<exact_ceil_integral_t<T>>>(val) >> shift);
12,748✔
454
}
6,273✔
455

456
enum class _mask_len {
457
  unknown,
458
  in_range
459
};
460

461
template <std::integral T, _mask_len len_in_range = _mask_len::in_range, typename size_type = size_t>
462
constexpr T _mask(const size_type len) {
45,312,424✔
463
  constexpr std::make_unsigned_t<T> one = std::make_unsigned_t<T>(1);
45,312,424✔
464
  if constexpr (len_in_range != _mask_len::unknown) {
21,819,529✔
465
    return static_cast<T>((one << len) - one);
49,272✔
466
  } else {
21,795,136✔
467
    // The digits_mask is solely here to prevent Undefined Sanitizer
468
    // complaining about shift of len >= digits
469
    // Note: on -O1 the (len & digits_mask) is optimized to simply (len)
470
    constexpr std::make_unsigned_t<T> digits_mask = bitsof<T>() - one;
45,263,152✔
471
    return static_cast<T>((one << (len & digits_mask)) * (len < bitsof<T>()) - one);
45,263,152✔
472
  }
21,795,136✔
473
}
21,819,529✔
474

475
// ------------- IMPLEMENTATION DETAILS: UTILITIES: ASSERTIONS -------------- //
476
// If the range allows multipass iteration, checks if last - first >= 0
477
template <class Iterator>
478
constexpr bool _assert_range_viability(Iterator first, Iterator last) {
1,020,957✔
479
  using traits_t = std::iterator_traits<Iterator>;
506,531✔
480
  using category_t = typename traits_t::iterator_category;
506,531✔
481
  using multi_t = std::forward_iterator_tag;
506,531✔
482
  constexpr bool is_multipass = std::is_base_of<multi_t, category_t>::value;
1,020,957✔
483
  const bool is_viable = !is_multipass || std::distance(first, last) >= 0;
1,535,383!
484
  assert(is_viable);
1,020,957!
485
  return is_viable;
1,535,383✔
486
}
506,531✔
487
// -------------------------------------------------------------------------- //
488

489
// --------- IMPLEMENTATION DETAILS: INSTRUCTIONS: POPULATION COUNT --------- //
490
// Counts the number of bits set to 1 with compiler intrinsics
491
template <class T, class>
492
constexpr T _popcnt(T src) noexcept {
105,216✔
493
  static_assert(binary_digits<T>::value, "");
52,544✔
494
  constexpr T digits = binary_digits<T>::value;
105,216✔
495
  if (digits <= std::numeric_limits<unsigned int>::digits) {
52,544✔
496
    src = __builtin_popcount(static_cast<std::make_unsigned_t<T>>(src));
48,384✔
497
  } else if (digits <= std::numeric_limits<unsigned long int>::digits) {
28,416✔
498
    src = __builtin_popcountl(static_cast<std::make_unsigned_t<T>>(src));
56,832✔
499
  } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
28,416✔
UNCOV
500
    src = __builtin_popcountll(static_cast<std::make_unsigned_t<T>>(src));
UNCOV
501
  } else {
UNCOV
502
    src = _popcnt(src, std::ignore);
UNCOV
503
  }
504
  return src;
105,216✔
505
}
52,544✔
506

507
// Counts the number of bits set to 1 without compiler intrinsics
508
template <class T, class... X>
UNCOV
509
constexpr T _popcnt(T src, X...) noexcept {
UNCOV
510
  static_assert(binary_digits<T>::value, "");
UNCOV
511
  T dst = T();
UNCOV
512
  for (dst = T(); src; src = lsr(src, 1)) {
UNCOV
513
    dst += src & 1;
UNCOV
514
  }
UNCOV
515
  return dst;
UNCOV
516
}
517
// -------------------------------------------------------------------------- //
518

519
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: LEADING ZEROS COUNT -------- //
520
// Counts the number of leading zeros with compiler intrinsics
521
template <class T, class>
522
constexpr T _lzcnt(T src) noexcept {
523
  static_assert(binary_digits<T>::value, "");
524
  constexpr T digits = binary_digits<T>::value;
525
  T dst = T();
526
  if (digits < std::numeric_limits<unsigned int>::digits) {
527
    dst = src ? __builtin_clz(src) - (std::numeric_limits<unsigned int>::digits - digits)
528
              : digits;
529
  } else if (digits == std::numeric_limits<unsigned int>::digits) {
530
    dst = src ? __builtin_clz(src) : digits;
531
  } else if (digits < std::numeric_limits<unsigned long int>::digits) {
532
    dst = src ? __builtin_clzl(src) - (std::numeric_limits<unsigned long int>::digits - digits)
533
              : digits;
534
  } else if (digits == std::numeric_limits<unsigned long int>::digits) {
535
    dst = src ? __builtin_clzl(src) : digits;
536
  } else if (digits < std::numeric_limits<unsigned long long int>::digits) {
537
    dst = src ? __builtin_clzll(src) - (std::numeric_limits<unsigned long long int>::digits - digits)
538
              : digits;
539
  } else if (digits == std::numeric_limits<unsigned long long int>::digits) {
540
    dst = src ? __builtin_clzll(src) : digits;
541
  } else {
542
    dst = _lzcnt(src, std::ignore);
543
  }
544
  return dst;
545
}
546

547
// Counts the number of leading zeros without compiler intrinsics
548
template <class T, class... X>
549
constexpr T _lzcnt(T src, X...) noexcept {
550
  static_assert(binary_digits<T>::value, "");
551
  constexpr T digits = binary_digits<T>::value;
552
  T dst = src != T();
553
  while ((src = lsr(src, 1))) {
554
    ++dst;
555
  }
556
  return digits - dst;
557
}
558
// -------------------------------------------------------------------------- //
559

560
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: TRAILING ZEROS COUNT ------- //
561
// Counts the number of trailing zeros with compiler intrinsics
562
template <class T, class>
563
constexpr T _tzcnt(T src) noexcept {
66,558✔
564
  static_assert(binary_digits<T>::value, "");
33,231✔
565
  constexpr T digits = binary_digits<T>::value;
66,558✔
566
  T dst = T();
66,558✔
567
  if (digits <= std::numeric_limits<unsigned int>::digits) {
33,231✔
568
    dst = src ? __builtin_ctz(src) : digits;
32,488!
569
  } else if (digits <= std::numeric_limits<unsigned long int>::digits) {
17,035✔
570
    dst = src ? __builtin_ctzl(src) : digits;
34,070!
571
  } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
17,035✔
UNCOV
572
    dst = src ? __builtin_ctzll(src) : digits;
×
UNCOV
573
  } else {
UNCOV
574
    dst = _tzcnt(src, std::ignore);
UNCOV
575
  }
576
  return dst;
66,558✔
577
}
33,231✔
578

579
// Counts the number of trailing zeros without compiler intrinsics
580
template <class T, class... X>
UNCOV
581
constexpr T _tzcnt(T src, X...) noexcept {
UNCOV
582
  static_assert(binary_digits<T>::value, "");
UNCOV
583
  constexpr T digits = binary_digits<T>::value;
UNCOV
584
  T dst = digits;
UNCOV
585
  if (src) {
UNCOV
586
    src = lsr((src ^ (src - 1)), 1);
UNCOV
587
    for (dst = T(); src; dst++) {
UNCOV
588
      src = lsr(src, 1);
UNCOV
589
    }
UNCOV
590
  }
UNCOV
591
  return dst;
UNCOV
592
}
593
// -------------------------------------------------------------------------- //
594

595
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT FIELD EXTRACTION ------- //
596
// Extacts to lsbs a field of contiguous bits with compiler intrinsics
597
template <class T, class>
598
constexpr T _bextr(T src, T start, T len) noexcept {
599
  static_assert(binary_digits<T>::value, "");
600
  constexpr T digits = binary_digits<T>::value;
601
  T dst = T();
602
  if (digits <= std::numeric_limits<unsigned int>::digits) {
603
    dst = __builtin_ia32_bextr_u32(src, start, len);
604
  } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
605
    dst = __builtin_ia32_bextr_u64(src, start, len);
606
  } else {
607
    dst = _bextr(src, start, len, std::ignore);
608
  }
609
  return dst;
610
}
611

612
// Extacts to lsbs a field of contiguous bits without compiler intrinsics
613
template <class T, class... X>
614
constexpr T _bextr(T src, T start, T len, X...) noexcept {
15,104✔
615
  static_assert(binary_digits<T>::value, "");
7,616✔
616
  constexpr T digits = binary_digits<T>::value;
15,104✔
617
  constexpr T one = 1;
15,104✔
618
  const T msk = (one << len) * (len < digits) - one;
15,104✔
619
  return (lsr(src, start)) & msk * (start < digits);
15,104✔
620
}
7,616✔
621
// -------------------------------------------------------------------------- //
622

623
#if 0
624
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: PARALLEL BIT DEPOSIT ------- //
625
// Deposits bits according to a mask with compiler instrinsics
626
template <class T, class>
627
constexpr T _pdep(T src, T msk) noexcept {
628
  static_assert(binary_digits<T>::value, "");
629
  constexpr T digits = binary_digits<T>::value;
630
  T dst = T();
631
  if (digits <= std::numeric_limits<unsigned int>::digits) {
632
    dst = _pdep_u32(src, msk);
633
  } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
634
    dst = _pdep_u64(src, msk);
635
  } else {
636
    dst = _pdep(src, msk, std::ignore);
637
  }
638
  return dst;
639
}
640

641
// Deposits bits according to a mask without compiler instrinsics
642
template <class T, class... X>
643
constexpr T _pdep(T src, T msk, X...) noexcept {
644
  static_assert(binary_digits<T>::value, "");
645
  constexpr T digits = binary_digits<T>::value;
646
  T dst = T();
647
  T cnt = T();
648
  while (msk) {
649
    dst = lsr(dst, 1);
650
    if (msk & 1) {
651
      dst |= src << (digits - 1);
652
      src = lsr(src, 1);
653
    }
654
    msk = lsr(msk, 1);
655
    ++cnt;
656
  }
657
  dst = lsr(dst, (digits - cnt) * (cnt > 0));
658
  return dst;
659
}
660
// -------------------------------------------------------------------------- //
661

662
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: PARALLEL BIT EXTRACT ------- //
663
// Extracts bits according to a mask with compiler instrinsics
664
template <class T, class>
665
constexpr T _pext(T src, T msk) noexcept {
666
  static_assert(binary_digits<T>::value, "");
667
  constexpr T digits = binary_digits<T>::value;
668
  T dst = T();
669
  if (digits <= std::numeric_limits<unsigned int>::digits) {
670
    dst = _pext_u32(src, msk);
671
  } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
672
    dst = _pext_u64(src, msk);
673
  } else {
674
    dst = _pext(src, msk, std::ignore);
675
  }
676
  return dst;
677
}
678

679
// Extracts bits according to a mask without compiler instrinsics
680
template <class T, class... X>
681
constexpr T _pext(T src, T msk, X...) noexcept {
682
  static_assert(binary_digits<T>::value, "");
683
  constexpr T digits = binary_digits<T>::value;
684
  T dst = T();
685
  T cnt = T();
686
  while (msk) {
687
    if (msk & 1) {
688
      dst = lsr(dst, 1);
689
      dst |= src << (digits - 1);
690
      ++cnt;
691
    }
692
    src = lsr(src, 1);
693
    msk = lsr(msk, 1);
694
  }
695
  dst = lsr(dst, (digits - cnt) * (cnt > 0));
696
  return dst;
697
}
698
// -------------------------------------------------------------------------- //
699

700
// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: BYTE SWAP ------------- //
701
// Reverses the order of the underlying bytes with compiler intrinsics
702
template <class T, class T128>
703
constexpr T _byteswap(T src) noexcept {
704
  static_assert(binary_digits<T>::value, "");
705
  using byte_t = unsigned char;
706
  constexpr T digits = sizeof(T) * std::numeric_limits<byte_t>::digits;
707
  std::uint64_t tmp64 = 0;
708
  std::uint64_t* ptr64 = nullptr;
709
  if (std::is_same<T, T128>::value) {
710
    ptr64 = reinterpret_cast<std::uint64_t*>(&src);
711
    tmp64 = __builtin_bswap64(*ptr64);
712
    *ptr64 = __builtin_bswap64(*(ptr64 + 1));
713
    *(ptr64 + 1) = tmp64;
714
  } else if (digits == std::numeric_limits<std::uint16_t>::digits) {
715
    src = __builtin_bswap16(src);
716
  } else if (digits == std::numeric_limits<std::uint32_t>::digits) {
717
    src = __builtin_bswap32(src);
718
  } else if (digits == std::numeric_limits<std::uint64_t>::digits) {
719
    src = __builtin_bswap64(src);
720
  } else if (digits > std::numeric_limits<byte_t>::digits) {
721
    src = _byteswap(src, std::ignore);
722
  }
723
  return src;
724
}
725

726
// Reverses the order of the underlying bytes without compiler intrinsics
727
template <class T, class... X>
728
constexpr T _byteswap(T src, X...) noexcept {
729
  static_assert(binary_digits<T>::value, "");
730
  using byte_t = unsigned char;
731
  constexpr T half = sizeof(T) / 2;
732
  constexpr T end = sizeof(T) - 1;
733
  unsigned char* bytes = reinterpret_cast<byte_t*>(&src);
734
  unsigned char byte = 0;
735
  for (T i = T(); i < half; ++i) {
736
    byte = bytes[i];
737
    bytes[i] = bytes[end - i];
738
    bytes[end - i] = byte;
739
  }
740
  return src;
741
}
742
// -------------------------------------------------------------------------- //
743
#endif
744

745
// ------------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT SWAP ------------- //
746
// Reverses the order of the bits with or without of compiler intrinsics
747
template <class T>
748
constexpr T _bitswap(T src) noexcept {
21,042,880✔
749
  static_assert(binary_digits<T>::value, "");
10,521,728✔
750
  using byte_t = unsigned char;
10,521,728✔
751
  constexpr auto ignore = nullptr;
21,042,880✔
752
  constexpr T digits = binary_digits<T>::value;
21,042,880✔
753
  constexpr unsigned long long int first = 0x80200802ULL;
21,042,880✔
754
  constexpr unsigned long long int second = 0x0884422110ULL;
21,042,880✔
755
  constexpr unsigned long long int third = 0x0101010101ULL;
21,042,880✔
756
  constexpr unsigned long long int fourth = 32;
21,042,880✔
757
  constexpr bool is_size1 = sizeof(T) == 1;
21,042,880✔
758
  constexpr bool is_byte = digits == std::numeric_limits<byte_t>::digits;
21,042,880✔
759
  constexpr bool is_octet = std::numeric_limits<byte_t>::digits == 8;
21,042,880✔
760
  constexpr bool is_pow2 = _popcnt(digits, ignore) == 1;
21,042,880✔
761
  T dst = src;
21,042,880✔
762
  T i = digits - 1;
21,042,880✔
763
  if (is_size1 && is_byte && is_octet) {
10,521,728✔
764
    dst = static_cast<T>(lsr(((static_cast<std::make_unsigned_t<T>>(src) * first) & second) * third, fourth));
5,245,200✔
765
  } else if (is_pow2) {
7,899,296✔
766
    dst = _bitswap<T, digits>(src);
15,797,680✔
767
  } else {
7,899,296✔
UNCOV
768
    for (src = lsr(src, 1); src; src = lsr(src, 1)) {
×
UNCOV
769
      dst <<= 1;
UNCOV
770
      dst |= src & 1;
UNCOV
771
      i--;
UNCOV
772
    }
UNCOV
773
    dst <<= i;
UNCOV
774
  }
775
  return dst;
21,042,880✔
776
}
10,521,728✔
777

778
// Reverses the order of the bits: recursive metafunction
779
template <class T, std::size_t N>
780
constexpr T _bitswap(T src) noexcept {
79,020,032✔
781
  static_assert(binary_digits<T>::value, "");
39,511,840✔
782
  constexpr T cnt = N >> 1;
79,020,032✔
783
  constexpr T msk = _bitswap<T, cnt>();
79,020,032✔
784
  src = ((lsr(src, cnt)) & msk) | ((src << cnt) & ~msk);
79,020,032✔
785
  return cnt > 1 ? _bitswap<T, cnt>(src) : src;
79,020,032✔
786
}
39,511,840✔
787

788
// Reverses the order of the bits: mask for the recursive metafunction
789
template <class T, std::size_t N>
UNCOV
790
constexpr T _bitswap() noexcept {
UNCOV
791
  static_assert(binary_digits<T>::value, "");
UNCOV
792
  constexpr T digits = binary_digits<T>::value;
UNCOV
793
  T cnt = digits;
UNCOV
794
  T msk = ~T();
UNCOV
795
  while (cnt != N) {
UNCOV
796
    cnt = lsr(cnt, 1);
UNCOV
797
    msk ^= (msk << cnt);
UNCOV
798
  }
UNCOV
799
  return msk;
UNCOV
800
}
801
// -------------------------------------------------------------------------- //
802

803
// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT BLEND ------------- //
804
// Replaces bits of src0 by the ones of src1 where the mask is true
805

806
template <typename T, typename U>
807
constexpr exact_ceil_integral_t<T> _bitblend(
11,659✔
808
    const T src0_,
809
    const U src1_,
810
    const exact_ceil_integral_t<T> msk) noexcept
811
  requires(std::is_same_v<exact_ceil_integral_t<T>, exact_ceil_integral_t<U>>)
812
{
11,465✔
813
  static_assert(binary_digits<exact_ceil_integral_t<T>>::value, "");
11,465✔
814
  const exact_ceil_integral_t<T> src0 = static_cast<exact_ceil_integral_t<T>>(src0_);
23,124✔
815
  const exact_ceil_integral_t<U> src1 = static_cast<exact_ceil_integral_t<U>>(src1_);
23,124✔
816
  return src0 ^ ((src0 ^ src1) & msk);
23,124✔
817
}
11,465✔
818

819
// Replaces len bits of src0 by the ones of src1 starting at start
820
template <typename T, typename U>
821
constexpr exact_ceil_integral_t<T> _bitblend(
3,155,776✔
822
    const T src0_,
823
    const U src1_,
824
    const exact_ceil_integral_t<T> start,
825
    const exact_ceil_integral_t<T> len) noexcept
826
  requires(std::is_same_v<exact_ceil_integral_t<T>, exact_ceil_integral_t<U>>)
827
{
1,198,292✔
828
  static_assert(binary_digits<exact_ceil_integral_t<T>>::value, "");
1,198,292✔
829
  constexpr exact_ceil_integral_t<T> digits = bitsof<exact_ceil_integral_t<T>>();
4,354,068✔
830
  const exact_ceil_integral_t<T> src0 = static_cast<exact_ceil_integral_t<T>>(src0_);
4,354,068✔
831
  const exact_ceil_integral_t<U> src1 = static_cast<exact_ceil_integral_t<U>>(src1_);
4,354,068✔
832
  const exact_ceil_integral_t<T> msk = _mask<exact_ceil_integral_t<T>, _mask_len::unknown>(len) << start;
4,354,068✔
833
  return src0 ^ ((src0 ^ src1) & msk * (start < digits));
4,354,068✔
834
}
1,198,292✔
835
// -------------------------------------------------------------------------- //
836

837
// ---------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT EXCHANGE ------------ //
838
// Exchanges/swaps bits of src0 by the ones of src1 where the mask is true
839
template <class T>
840
constexpr void _bitexch(T& src0, T& src1, T msk) noexcept {
841
  src0 = src0 ^ static_cast<T>(src1 & msk);
842
  src1 = src1 ^ static_cast<T>(src0 & msk);
843
  src0 = src0 ^ static_cast<T>(src1 & msk);
844
  return;
845
}
846

847
// Replaces len bits of src0 by the ones of src1 starting at start
848
template <class T, class S>
849
constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept {
7,494✔
850
  static_assert(binary_digits<T>::value, "");
2,370✔
851
  constexpr auto digits = binary_digits<T>::value;
7,494✔
852
  const T msk = (len < digits)
7,494!
853
                    ? _mask<T, _mask_len::unknown>(len) << start
7,470✔
854
                    : -1;  // TODO: What if start > 0 here?
2,370✔
855
  src0 = src0 ^ static_cast<T>(src1 & msk);
7,494✔
856
  src1 = src1 ^ static_cast<T>(src0 & msk);
7,494✔
857
  src0 = src0 ^ static_cast<T>(src1 & msk);
7,494✔
858
  return;
7,494✔
859
}
2,370✔
860

861
// Replaces len bits of src0 by the ones of src1 starting at start0
862
// in src0 and start1 in src1.
863
// len <= digits-max(start0, start1)
864
// clang-format off
865
template <class T, class S>
866
constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept
20,307,140✔
867
{
20,594,587✔
868
    static_assert(binary_digits<T>::value, "");
20,594,587✔
869
    constexpr auto digits = binary_digits<T>::value;
40,901,727✔
870
    const T msk = _mask<T, _mask_len::unknown>(len);
40,901,727✔
871
    if (start0 >= start1) {
40,901,727✔
872
        src0 = src0 ^ (
30,612,099✔
873
                static_cast<T>(src1 << (start0 - start1))
20,455,439✔
874
                &
20,455,439✔
875
                static_cast<T>(msk << start0)
30,612,099✔
876
        );
10,298,779✔
877
        src1 = src1 ^ (
20,455,439✔
878
                static_cast<T>(lsr(src0, (start0 - start1)))
20,455,439✔
879
                &
20,455,439✔
880
                static_cast<T>(msk << start1)
30,612,099✔
881
        );
10,298,779✔
882
        src0 = src0 ^ (
40,768,759✔
883
                static_cast<T>(src1 << (start0 - start1))
20,455,439✔
884
                &
20,455,439✔
885
                static_cast<T>(msk << start0)
30,612,099✔
886
        );
10,298,779✔
887
    } else {
10,298,779✔
888
        src0 = src0 ^ (
20,446,288✔
889
                static_cast<T>(lsr(src1, (start1 - start0)))
20,446,288✔
890
                &
20,446,288✔
891
                static_cast<T>(msk << start0)
30,596,768✔
892
        );
10,295,808✔
893
        src1 = src1 ^ (
30,596,768✔
894
                static_cast<T>(src0 << (start1 - start0))
20,446,288✔
895
                &
20,446,288✔
896
                static_cast<T>(msk << start1)
30,596,768✔
897
        );
10,295,808✔
898
        src0 = src0 ^ (
30,596,768✔
899
                static_cast<T>(lsr(src1, (start1 - start0)))
20,446,288✔
900
                &
20,446,288✔
901
                static_cast<T>(msk << start0)
30,596,768✔
902
        );
10,295,808✔
903
    }
10,295,808✔
904
    return;
40,901,727✔
905
}
20,594,587✔
906
// clang-format on
907
// -------------------------------------------------------------------------- //
908

909
// ----------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT COMPARE ------------ //
910
// Compares a subsequence of bits within src0 and src1 and returns 0 if equal
911
template <class T>
912
constexpr T _bitcmp(T src0, T src1, T start0, T start1, T len) noexcept {
913
  static_assert(binary_digits<T>::value, "");
914
  return _bextr(src0, start0, len) == _bextr(src1, start1, len);
915
}
916
// -------------------------------------------------------------------------- //
917

918
// --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT LEFT ---- //
919
// Left shifts dst by cnt bits, filling the lsbs of dst by the msbs of src
920
template <class T>
921
constexpr T _shld(T dst, T src, T cnt) noexcept {
922
  static_assert(binary_digits<T>::value, "");
923
  constexpr T digits = binary_digits<T>::value;
924
  if (cnt < digits) {
925
    dst = (dst << cnt) | (lsr(src, (digits - cnt)));
926
  } else {
927
    dst = (src << (cnt - digits)) * (cnt < digits + digits);
928
  }
929
  return dst;
930
}
931
// -------------------------------------------------------------------------- //
932

933
// --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT RIGHT --- //
934
// Right shifts dst by cnt bits, filling the msbs of dst by the lsbs of src
935
template <class T>
936
constexpr T _shrd(T dst, T src, T cnt) noexcept {
22,279,610✔
937
  static_assert(binary_digits<T>::value, "");
11,838,264✔
938
  constexpr T digits = binary_digits<T>::value;
22,279,610✔
939
  if (cnt < digits) {
22,279,610!
940
    dst = (lsr(dst, cnt)) | (src << (digits - cnt));
22,279,610✔
941
  } else {
11,838,264✔
942
    dst = (lsr(src, (cnt - digits))) * (cnt < digits + digits);
×
UNCOV
943
  }
944
  return dst;
22,279,610✔
945
}
11,838,264✔
946
// -------------------------------------------------------------------------- //
947

948
#if defined(__ADX__)
949
template <bool Add>
950
unsigned char ADDCARRYSUBBORROW32(unsigned char c, uint32_t a, uint32_t b, uint32_t* out) {
951
  return (Add ? _addcarryx_u32(c, a, b, out) : _subborrow_u32(c, a, b, out));
952
}
953
template <bool Add>
954
unsigned char ADDCARRYSUBBORROW64(unsigned char c, uint64_t a, uint64_t b, uint64_t* out) {
955
  static_assert(sizeof(uint64_t) == sizeof(unsigned long long int));
956
  return (Add ? _addcarryx_u64(c, a, b, reinterpret_cast<unsigned long long int*>(out)) : _subborrow_u64(c, a, b, reinterpret_cast<unsigned long long int*>(out)));
957
}
958
#else
959
template <bool Add>
960
unsigned char ADDCARRYSUBBORROW32(unsigned char c, uint32_t a, uint32_t b, uint32_t* out) {
961
  return (Add ? _addcarry_u32(c, a, b, out) : _subborrow_u32(c, a, b, out));
962
}
963
template <bool Add>
964
unsigned char ADDCARRYSUBBORROW64(unsigned char c, uint64_t a, uint64_t b, uint64_t* out) {
965
  static_assert(sizeof(uint64_t) == sizeof(unsigned long long int));
966
  return (Add ? _addcarry_u64(c, a, b, reinterpret_cast<unsigned long long int*>(out)) : _subborrow_u64(c, a, b, reinterpret_cast<unsigned long long int*>(out)));
967
}
968
#endif
969

970
template <bool Add, std::integral U>
971
static inline unsigned char add_carry_sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept {
972
  if constexpr (32 > bitsof<U>()) {
973
    // a       [aaaaaaaa111111111111111111111111111]
974
    // b     + [bbbbbbbb000000000000000000000000000]
975
    // carry +                            [0000000c]
976
    const uint8_t shift = (32 - bitsof<U>());
977
    uint32_t carry_propagation = Add ? ((1 << shift) - 1) : 0;
978
    uint32_t tmp_out;
979
    unsigned char carry = ADDCARRYSUBBORROW32<Add>(
980
        c_in,
981
        (static_cast<uint32_t>(a) << shift) | carry_propagation,
982
        (static_cast<uint32_t>(b) << shift),
983
        &tmp_out);
984
    *out = static_cast<U>(tmp_out >> shift);
985
    return carry;
986
  } else if constexpr (32 == bitsof<U>()) {
987
    return ADDCARRYSUBBORROW32<Add>(c_in, static_cast<uint32_t>(a), static_cast<uint32_t>(b), reinterpret_cast<uint32_t>(out));
988
  } else if constexpr (64 == bitsof<U>()) {
989
    return ADDCARRYSUBBORROW64<Add>(c_in, static_cast<uint64_t>(a), static_cast<uint64_t>(b), reinterpret_cast<uint64_t>(out));
990
  } else if constexpr (0 == (bitsof<U>() % 64)) {
991
    using t64 = std::conditional<std::is_signed_v<U>, int64_t, uint64_t>;
992
    unsigned char carry;
993
    for (int i = 0; i < (bitsof<U>() / 64); i++) {
994
      carry = ADDCARRYSUBBORROW64<Add>(c_in, static_cast<t64>(a >> (i * 64)), static_cast<t64>(b >> (i * 64)), reinterpret_cast<t64>(out) + i);
995
    }
996
    return carry;
997
  } else {
998
    assert(((void)"add carry intrinsics support only support powers of 2 bits", false));
999
  }
1000
}
1001

1002
template <std::integral U>
1003
static inline unsigned char add_carry(unsigned char c_in, U a, U b, U* out) noexcept {
1004
  return add_carry_sub_borrow<true, U>(c_in, a, b, out);
1005
}
1006

1007
template <std::integral U>
1008
static inline unsigned char sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept {
1009
  return add_carry_sub_borrow<false, U>(c_in, a, b, out);
1010
}
1011

1012
// -------------------------------------------------------------------------- //
1013

1014
// -------- IMPLEMENTATION DETAILS: INSTRUCTIONS: MULTIWORD MULTIPLY -------- //
1015
// Multiplies src0 and src1 and gets the full result with compiler intrinsics
1016
template <class T, class T128>
1017
constexpr T _mulx(T src0, T src1, T* hi) noexcept
1018
{
1019
    static_assert(binary_digits<T>::value, "");
1020
    using wider_t = ceil_integral<bitsof<T>() + bitsof<T>()>;
1021
    constexpr T digits = binary_digits<T>::value;
1022
    wider_t tmp = 0;
1023
    T128 tmp128 = 0;
1024
    T lo = 0;
1025
    if (digits == std::numeric_limits<std::uint64_t>::digits) {
1026
        tmp128 = static_cast<T128>(src0) * static_cast<T128>(src1);
1027
        *hi = tmp128 >> digits;
1028
        lo = tmp128;
1029
    } else if (digits + digits == binary_digits<wider_t>::value) {
1030
        tmp = static_cast<wider_t>(src0) * static_cast<wider_t>(src1);
1031
        *hi = tmp >> digits;
1032
        lo = tmp;
1033
    } else {
1034
        lo = _mulx(src0, src1, hi, std::ignore);
1035
    }
1036
    return lo;
1037
}
1038

1039
// Multiplies src0 and src1 and gets the full result without compiler intrinsics
1040
template <class T, class... X>
1041
constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept
1042
{
1043
    static_assert(binary_digits<T>::value, "");
1044
    constexpr T digits = binary_digits<T>::value;
1045
    constexpr T offset = digits / 2;
1046
    constexpr T ones = ~static_cast<T>(0);
1047
    const T lsbs0 = src0 & static_cast<T>(lsr(ones, (digits - offset)));
1048
    const T msbs0 = lsr(src0, offset);
1049
    const T lsbs1 = src1 & static_cast<T>(lsr(ones, (digits - offset)));
1050
    const T msbs1 = lsr(src1, offset);
1051
    const T llsbs = lsbs0 * lsbs1;
1052
    const T mlsbs = msbs0 * lsbs1;
1053
    const T lmsbs = lsbs0 * msbs1;
1054
    const T mi = mlsbs + lmsbs;
1055
    const T lo = llsbs + static_cast<T>(mi << offset);
1056
    const T lcarry = lo < llsbs || lo < static_cast<T>(mi << offset);
1057
    const T mcarry = static_cast<T>(mi < mlsbs || mi < lmsbs) << offset;
1058
    *hi = static_cast<T>(lsr(mi, offset)) + msbs0 * msbs1 + mcarry + lcarry;
1059
    return lo;
1060
}
1061
// -------------------------------------------------------------------------- //
1062

1063
template <typename AlgoFunc, typename SrcIt, typename DstIt>
1064
constexpr auto with_bit_iterator_adapter(
19,381✔
1065
    bit_iterator<SrcIt> first,
1066
    bit_iterator<SrcIt> last,
1067
    bit_iterator<DstIt> d_first) {
19,381✔
1068
  using dst_word_type = typename bit_iterator<DstIt>::word_type;
19,381✔
1069
  using src_word_type = typename bit_iterator<SrcIt>::word_type;
19,381✔
1070
  if constexpr (!std::is_same_v<src_word_type, dst_word_type> && bitsof<src_word_type>() != bitsof<dst_word_type>()) {
19,381✔
1071
    if constexpr (bitsof<src_word_type>() > bitsof<dst_word_type>()) {
9,051✔
1072
      bit_iterator<bit_word_pointer_adapter<DstIt, SrcIt>> adapted_first(
18,116✔
1073
          bit_word_pointer_adapter<DstIt, SrcIt>(first.base(), first.position() / bitsof<dst_word_type>()), first.position() % bitsof<dst_word_type>());
13,587✔
1074
      bit_iterator<bit_word_pointer_adapter<DstIt, SrcIt>> adapted_last(
18,116✔
1075
          bit_word_pointer_adapter<DstIt, SrcIt>(last.base(), last.position() / bitsof<dst_word_type>()), last.position() % bitsof<dst_word_type>());
13,587✔
1076
      return AlgoFunc{}(adapted_first, adapted_last, d_first);
13,587✔
1077
    } else {
4,529✔
1078
      bit_iterator<bit_word_pointer_adapter<SrcIt, DstIt>> adapted_d_first(
18,088✔
1079
          bit_word_pointer_adapter<SrcIt, DstIt>(d_first.base(), d_first.position() / bitsof<src_word_type>()), d_first.position() % bitsof<src_word_type>());
13,566✔
1080
      return AlgoFunc{}(first, last, adapted_d_first);
13,566✔
1081
    }
4,522✔
1082
  } else {
10,330✔
1083
    return AlgoFunc{}(first, last, d_first);
30,990✔
1084
  }
10,330✔
1085
}
19,381✔
1086

1087
template <typename AlgoFunc, typename SrcIt, typename DstIt>
1088
constexpr auto with_bit_iterator_adapter(
1089
    bit_iterator<SrcIt> first,
1090
    bit_iterator<DstIt> last) {
1091
  using dst_word_type = typename bit_iterator<DstIt>::word_type;
1092
  using src_word_type = typename bit_iterator<SrcIt>::word_type;
1093
  if constexpr (!std::is_same_v<src_word_type, dst_word_type> && bitsof<src_word_type>() != bitsof<dst_word_type>()) {
1094
    if constexpr (bitsof<src_word_type>() > bitsof<dst_word_type>()) {
1095
      bit_iterator<bit_word_pointer_adapter<DstIt, SrcIt>> adapted_first(
1096
          bit_word_pointer_adapter<DstIt, SrcIt>(first.base(), first.position() / bitsof<dst_word_type>()), first.position() % bitsof<dst_word_type>());
1097
      return AlgoFunc{}(adapted_first, last);
1098
    } else {
1099
      bit_iterator<bit_word_pointer_adapter<SrcIt, DstIt>> adapted_last(
1100
          bit_word_pointer_adapter<SrcIt, DstIt>(last.base(), last.position() / bitsof<src_word_type>()), last.position() % bitsof<src_word_type>());
1101
      return AlgoFunc{}(first, adapted_last);
1102
    }
1103
  } else {
1104
    return AlgoFunc{}(first, last);
1105
  }
1106
}
1107

1108
namespace detail {
1109

1110
struct uninitialized_t {
1111
  explicit uninitialized_t() = default;
1112
};
1113
inline constexpr uninitialized_t uninitialized{};
1114

1115
}  // namespace detail
1116

1117
// ========================================================================== //
1118
}  // namespace bit
1119
#endif // _BIT_DETAILS_HPP_INCLUDED
1120
// ========================================================================== //
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