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

PeterCDMcLean / BitLib / 16383376446

19 Jul 2025 01:20AM UTC coverage: 78.092% (-0.5%) from 78.608%
16383376446

Pull #18

github

web-flow
Merge eb5f6b9bf into 02a1d27a8
Pull Request #18: From string

3310 of 4840 branches covered (68.39%)

Branch coverage included in aggregate %.

181 of 217 new or added lines in 12 files covered. (83.41%)

12 existing lines in 2 files now uncovered.

2404 of 2477 relevant lines covered (97.05%)

31964982.86 hits per line

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

76.5
/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

19
#if __has_include(<immintrin.h>)
20
#include <immintrin.h>
21
#else
22
#define NO_X86_INTRINSICS
23
#endif
24

25
#include <algorithm>
26
#include <bit>
27
#include <cassert>
28
#include <concepts>
29
#include <cstddef>
30
#include <cstdint>
31
#include <iterator>
32
#include <limits>
33
#include <ranges>
34
#include <stdexcept>
35
#include <tuple>
36
#include <type_traits>
37
#include <utility>
38

39
#include "bitlib/bit-containers/bit_bitsof.hpp"
40
#include "bitlib/bit_concepts.hpp"
41

42
// Project sources
43
// Third-party libraries
44
// Miscellaneous
45
namespace bit {
46
class bit_value;
47
template <class WordType>
48
class bit_reference;
49
template <class Iterator>
50
class bit_iterator;
51
template <class WordType>
52
using bit_pointer = bit_iterator<WordType*>;
53
template <typename target_word_ptr, typename source_word_ptr>
54
class bit_word_pointer_adapter;
55

56
// ========================================================================== //
57

58
template <size_t N>
59
using ceil_integral = std::conditional_t<
60
    (N <= bitsof<std::uint8_t>()),
61
    std::uint8_t,
62
    std::conditional_t<
63
        (N <= bitsof<std::uint16_t>()),
64
        std::uint16_t,
65
        std::conditional_t<
66
            (N <= bitsof<std::uint32_t>()),
67
            std::uint32_t,
68
            std::conditional_t<
69
                (N <= bitsof<std::uint64_t>()),
70
                std::uint64_t,
71
                std::uint64_t>>>>;
72

73
/* *************** IMPLEMENTATION DETAILS: CV ITERATOR TRAITS *************** */
74
// Cv iterator traits structure definition
75
template <class Iterator>
76
struct _cv_iterator_traits
77
{
78
    // Assertions
79
    private:
80
    using _traits_t = std::iterator_traits<Iterator>;
81
    using _difference_t = typename _traits_t::difference_type;
82
    using _value_t = typename _traits_t::value_type;
83
    using _pointer_t = typename _traits_t::pointer;
84
    using _reference_t = typename _traits_t::reference;
85
    using _category_t =  typename _traits_t::iterator_category;
86
    using _no_pointer_t = typename std::remove_pointer<_pointer_t>::type;
87
    using _no_reference_t = typename std::remove_reference<_reference_t>::type;
88
    using _raw_value_t = typename std::remove_cv<_value_t>::type;
89
    using _raw_pointer_t = typename std::remove_cv<_no_pointer_t>::type;
90
    using _raw_reference_t = typename std::remove_cv<_no_reference_t>::type;
91
    using _cv_value_t = _no_reference_t;
92

93
    //    static_assert(std::is_same<_raw_pointer_t, _raw_value_t>::value, "");
94
    //    static_assert(std::is_same<_raw_reference_t, _raw_value_t>::value, "");
95

96
    // Types
97
    public:
98
    using difference_type = _difference_t;
99
    using value_type = _cv_value_t;
100
    using pointer = _pointer_t;
101
    using reference = _reference_t;
102
    using iterator_category = _category_t;
103
};
104
/* ************************************************************************** */
105

106
/*
107
exact_floor_integral is used to determine the exact integral type that a proxy reference
108
can be implicitly converted to.
109
If the proxy reference supports multiple types, it will pick the smallest, preferring unsigned.
110
*/
111
template <typename T>
112
struct exact_floor_integral {
113
 private:
114
  using U = std::remove_cvref_t<T>;
115

116
  template <typename From, typename To>
117
  static constexpr bool is_exactly_convertible() {
118
    if constexpr (!std::is_convertible_v<From, To>) {
119
      return false;
120
    } else {
121
      // Try brace-initialization to detect narrowing
122
      return requires(From f) {
123
        To{f};  // will fail if narrowing
124
      };
125
    }
126
  }
127

128
 public:
129
  using type =
130
      std::conditional_t<
131
          is_exactly_convertible<U, uint8_t>(), uint8_t,
132
          std::conditional_t<
133
              is_exactly_convertible<U, int8_t>(), int8_t,
134
              std::conditional_t<
135
                  is_exactly_convertible<U, uint16_t>(), uint16_t,
136
                  std::conditional_t<
137
                      is_exactly_convertible<U, int16_t>(), int16_t,
138
                      std::conditional_t<
139
                          is_exactly_convertible<U, uint32_t>(), uint32_t,
140
                          std::conditional_t<
141
                              is_exactly_convertible<U, int32_t>(), int32_t,
142
                              std::conditional_t<
143
                                  is_exactly_convertible<U, uint64_t>(), uint64_t,
144
                                  std::conditional_t<
145
                                      is_exactly_convertible<U, int64_t>(), int64_t,
146
                                      void>>>>>>>>;
147
};
148

149
// Helper alias
150
template <typename T>
151
using exact_floor_integral_t = typename exact_floor_integral<T>::type;
152

153
template <typename From, typename To, typename = void>
154
struct is_static_castable : std::false_type {};
155

156
template <typename From, typename To>
157
struct is_static_castable<From, To, std::void_t<decltype(static_cast<To>(std::declval<From>()))>>
158
    : std::true_type {};
159

160
template <typename From, typename To>
161
constexpr bool is_static_castable_v = is_static_castable<From, To>::value;
162

163
/* ***************************** BINARY DIGITS ****************************** */
164
// Binary digits structure definition
165
// Implementation template: only instantiates static_asserts for non-byte types.
166
template <typename T, bool = std::is_same<T, std::byte>::value>
167
struct binary_digits_impl : std::integral_constant<std::size_t, std::numeric_limits<std::make_unsigned_t<exact_floor_integral_t<T>>>::digits> {
168
  static_assert(std::is_integral<exact_floor_integral_t<T>>::value, "Type must be integral");
169
  //static_assert(std::is_unsigned<T>::value, "Type must be unsigned");
170
  static_assert(!std::is_same<exact_floor_integral_t<T>, bool>::value, "Type must not be bool");
171
  static_assert(!std::is_same<exact_floor_integral_t<T>, char>::value, "Type must not be char");
172
};
173

174
// Specialization for std::byte.
175
template <>
176
struct binary_digits_impl<std::byte, true> : std::integral_constant<std::size_t, std::numeric_limits<unsigned char>::digits> {};
177

178
// Public interface that removes cv-qualifiers.
179
template <typename UIntType>
180
struct binary_digits : binary_digits_impl<std::remove_cv_t<UIntType>> {};
181

182
// Binary digits value
183
template <class T>
184
constexpr std::size_t binary_digits_v = binary_digits<T>::value;
185
/*************************************************************************** */
186

187
/* ******************* IMPLEMENTATION DETAILS: UTILITIES ******************** */
188
// Assertions
189
template <class Iterator>
190
constexpr bool _assert_range_viability(Iterator first, Iterator last);
191
/* ************************************************************************** */
192

193
// Bit field extraction
194
template <class T, class = decltype(__builtin_ia32_bextr_u64(T(), T(), T()))>
195
constexpr T _bextr(T src, T start, T len) noexcept;
196
template <class T, class... X>
197
constexpr T _bextr(T src, T start, T len, X...) noexcept;
198

199
// Bit swap
200
template <class T>
201
constexpr T _bitswap(T src) noexcept;
202
template <class T, std::size_t N>
203
constexpr T _bitswap(T src) noexcept;
204
template <class T, std::size_t N>
205
constexpr T _bitswap() noexcept;
206

207
// Bit exchange
208
template <class T>
209
constexpr void _bitexch(T& src0, T& src1, T msk) noexcept;
210
template <class T, class S>
211
constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept;
212
template <class T, class S>
213
constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept;
214

215
// Double precision shift left
216
template <class T>
217
constexpr T _shld(T dst, T src, T cnt) noexcept;
218

219
// Double precision shift right
220
template <class T>
221
constexpr T _shrd(T dst, T src, T cnt) noexcept;
222

223
// Multiword multiply
224
template <typename T, typename T128 = decltype(__uint128_t(T()))>
225
constexpr T _mulx(T src0, T src1, T* hi) noexcept;
226
template <typename T, typename... X>
227
constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept;
228
template <typename T, typename T128 = decltype(__uint128_t(T()))>
229
constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder) noexcept;
230
template <typename T, typename... X>
231
constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder, X...) noexcept;
232
/* ************************************************************************** */
233

234
/*
235
Logical shift right
236
*/
237
template <std::integral T, typename size_type = size_t>
238
constexpr T lsr(const T val, const size_type shift) {
172,276,425✔
239
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
240
  assert(shift < bitsof<T>());
241
#endif
242
  return static_cast<T>(static_cast<std::make_unsigned_t<T>>(val) >> shift);
172,276,425✔
243
}
86,072,893✔
244

245
/*
246
Logic shift right when `val` operand is a proxy reference
247
*/
248
template <typename T, typename size_type = size_t>
249
constexpr exact_floor_integral_t<T> lsr(const T val, const size_type shift) {
12,740✔
250
  static_assert(!std::is_same_v<exact_floor_integral_t<T>, void>,
6,269✔
251
                "Type T must be convertible to an integral type");
6,269✔
252
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
253
  assert(shift < bitsof<exact_floor_integral_t<T>>());
254
#endif
255
  return static_cast<exact_floor_integral_t<T>>(static_cast<std::make_unsigned_t<exact_floor_integral_t<T>>>(val) >> shift);
12,740✔
256
}
6,269✔
257

258
/*
259
Logical shift left
260
*/
261
template <std::integral T, typename size_type = size_t>
262
constexpr T lsl(const T val, const size_type shift) {
22,279,610✔
263
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
264
  assert(shift < bitsof<T>());
265
#endif
266
  return static_cast<T>(static_cast<std::make_unsigned_t<T>>(val) << shift);
22,279,610✔
267
}
11,838,264✔
268

269
/*
270
Logic shift left when `val` operand is a proxy reference
271
*/
272
template <typename T, typename size_type = size_t>
273
constexpr exact_floor_integral_t<T> lsl(const T val, const size_type shift) {
274
  static_assert(!std::is_same_v<exact_floor_integral_t<T>, void>,
275
                "Type T must be convertible to an integral type");
276
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
277
  assert(shift < bitsof<exact_floor_integral_t<T>>());
278
#endif
279
  return static_cast<exact_floor_integral_t<T>>(static_cast<std::make_unsigned_t<exact_floor_integral_t<T>>>(val) << shift);
280
}
281

282
enum class _mask_len {
283
  unknown,
284
  in_range
285
};
286

287
template <std::integral T, _mask_len len_in_range = _mask_len::in_range, typename size_type = size_t>
288
constexpr T _mask(const size_type len) {
45,321,583✔
289
  constexpr std::make_unsigned_t<T> one = std::make_unsigned_t<T>(1);
45,321,583✔
290
  if constexpr (len_in_range != _mask_len::unknown) {
21,824,151✔
291
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
292
    assert(len < bitsof<T>());
293
#endif
294
    return static_cast<T>((one << len) - one);
58,405✔
295
  } else {
21,795,149✔
296
    // The digits_mask is solely here to prevent Undefined Sanitizer
297
    // complaining about shift of len >= digits
298
    // Note: on -O1 the (len & digits_mask) is optimized to simply (len)
299
    constexpr std::make_unsigned_t<T> digits_mask = bitsof<T>() - one;
45,263,178✔
300
    return static_cast<T>((one << (len & digits_mask)) * (len < bitsof<T>()) - one);
45,263,178✔
301
  }
21,795,149✔
302
}
21,824,151✔
303

304
// ------------- IMPLEMENTATION DETAILS: UTILITIES: ASSERTIONS -------------- //
305
// If the range allows multipass iteration, checks if last - first >= 0
306
template <class Iterator>
307
constexpr bool _assert_range_viability(Iterator first, Iterator last) {
1,026,271✔
308
  using traits_t = std::iterator_traits<Iterator>;
509,188✔
309
  using category_t = typename traits_t::iterator_category;
509,188✔
310
  using multi_t = std::forward_iterator_tag;
509,188✔
311
  constexpr bool is_multipass = std::is_base_of<multi_t, category_t>::value;
1,026,271✔
312
  const bool is_viable = !is_multipass || std::distance(first, last) >= 0;
1,543,354!
313
  assert(is_viable);
1,026,271!
314
  return is_viable;
1,543,354✔
315
}
509,188✔
316
// -------------------------------------------------------------------------- //
317

318
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT FIELD EXTRACTION ------- //
319
// Extacts to lsbs a field of contiguous bits with compiler intrinsics
320
template <class T, class>
321
constexpr T _bextr(T src, T start, T len) noexcept {
322
  static_assert(binary_digits<T>::value, "");
323
  constexpr T digits = binary_digits<T>::value;
324
  T dst = T();
325
  if (digits <= std::numeric_limits<unsigned int>::digits) {
326
    dst = __builtin_ia32_bextr_u32(src, start, len);
327
  } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
328
    dst = __builtin_ia32_bextr_u64(src, start, len);
329
  } else {
330
    dst = _bextr(src, start, len, std::ignore);
331
  }
332
  return dst;
333
}
334

335
// Extacts to lsbs a field of contiguous bits without compiler intrinsics
336
template <class T, class... X>
337
constexpr T _bextr(T src, T start, T len, X...) noexcept {
15,104✔
338
  static_assert(binary_digits<T>::value, "");
7,616✔
339
  constexpr T digits = binary_digits<T>::value;
15,104✔
340
  constexpr T one = 1;
15,104✔
341
  const T msk = (one << len) * (len < digits) - one;
15,104✔
342
  return (lsr(src, start)) & msk * (start < digits);
15,104✔
343
}
7,616✔
344
// -------------------------------------------------------------------------- //
345

346
// ------------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT SWAP ------------- //
347
// Reverses the order of the bits with or without of compiler intrinsics
348
template <class T>
349
constexpr T _bitswap(T src) noexcept {
21,042,880✔
350
  static_assert(binary_digits<T>::value, "");
10,521,728✔
351
  using byte_t = unsigned char;
10,521,728✔
352
  constexpr auto ignore = nullptr;
21,042,880✔
353
  constexpr T digits = binary_digits<T>::value;
21,042,880✔
354
  constexpr unsigned long long int first = 0x80200802ULL;
21,042,880✔
355
  constexpr unsigned long long int second = 0x0884422110ULL;
21,042,880✔
356
  constexpr unsigned long long int third = 0x0101010101ULL;
21,042,880✔
357
  constexpr unsigned long long int fourth = 32;
21,042,880✔
358
  constexpr bool is_size1 = sizeof(T) == 1;
21,042,880✔
359
  constexpr bool is_byte = digits == std::numeric_limits<byte_t>::digits;
21,042,880✔
360
  constexpr bool is_octet = std::numeric_limits<byte_t>::digits == 8;
21,042,880✔
361
  constexpr bool is_pow2 = std::has_single_bit(static_cast<std::make_unsigned_t<T>>(digits));
21,042,880✔
362
  T dst = src;
21,042,880✔
363
  T i = digits - 1;
21,042,880✔
364
  if (is_size1 && is_byte && is_octet) {
10,521,728✔
365
    dst = static_cast<T>(lsr(((static_cast<std::make_unsigned_t<T>>(src) * first) & second) * third, fourth));
5,245,200✔
366
  } else if (is_pow2) {
7,899,296✔
367
    dst = _bitswap<T, digits>(src);
15,797,680✔
368
  } else {
7,899,296✔
369
    for (src = lsr(src, 1); src; src = lsr(src, 1)) {
×
370
      dst <<= 1;
371
      dst |= src & 1;
372
      i--;
373
    }
374
    dst <<= i;
375
  }
376
  return dst;
21,042,880✔
377
}
10,521,728✔
378

379
// Reverses the order of the bits: recursive metafunction
380
template <class T, std::size_t N>
381
constexpr T _bitswap(T src) noexcept {
79,020,032✔
382
  static_assert(binary_digits<T>::value, "");
39,511,840✔
383
  constexpr T cnt = N >> 1;
79,020,032✔
384
  constexpr T msk = _bitswap<T, cnt>();
79,020,032✔
385
  src = ((lsr(src, cnt)) & msk) | ((src << cnt) & ~msk);
79,020,032✔
386
  return cnt > 1 ? _bitswap<T, cnt>(src) : src;
79,020,032✔
387
}
39,511,840✔
388

389
// Reverses the order of the bits: mask for the recursive metafunction
390
template <class T, std::size_t N>
391
constexpr T _bitswap() noexcept {
392
  static_assert(binary_digits<T>::value, "");
393
  constexpr T digits = binary_digits<T>::value;
394
  T cnt = digits;
395
  T msk = ~T();
396
  while (cnt != N) {
397
    cnt = lsr(cnt, 1);
398
    msk ^= (msk << cnt);
399
  }
400
  return msk;
401
}
402
// -------------------------------------------------------------------------- //
403

404
// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT BLEND ------------- //
405
// Replaces bits of src0 by the ones of src1 where the mask is true
406

407
template <typename T, typename U>
408
constexpr exact_floor_integral_t<T> _bitblend(
11,687✔
409
    const T src0_,
410
    const U src1_,
411
    const exact_floor_integral_t<T> msk) noexcept
412
  requires(std::is_same_v<exact_floor_integral_t<T>, exact_floor_integral_t<U>>)
413
{
11,493✔
414
  static_assert(binary_digits<exact_floor_integral_t<T>>::value, "");
11,493✔
415
  const exact_floor_integral_t<T> src0 = static_cast<exact_floor_integral_t<T>>(src0_);
23,180✔
416
  const exact_floor_integral_t<U> src1 = static_cast<exact_floor_integral_t<U>>(src1_);
23,180✔
417
  return src0 ^ ((src0 ^ src1) & msk);
23,180✔
418
}
11,493✔
419

420
// Replaces len bits of src0 by the ones of src1 starting at start
421
template <typename T, typename U>
422
constexpr exact_floor_integral_t<T> _bitblend(
3,155,789✔
423
    const T src0_,
424
    const U src1_,
425
    const exact_floor_integral_t<T> start,
426
    const exact_floor_integral_t<T> len) noexcept
427
  requires(std::is_same_v<exact_floor_integral_t<T>, exact_floor_integral_t<U>>)
428
{
1,198,305✔
429
  static_assert(binary_digits<exact_floor_integral_t<T>>::value, "");
1,198,305✔
430
  constexpr exact_floor_integral_t<T> digits = bitsof<exact_floor_integral_t<T>>();
4,354,094✔
431
  const exact_floor_integral_t<T> src0 = static_cast<exact_floor_integral_t<T>>(src0_);
4,354,094✔
432
  const exact_floor_integral_t<U> src1 = static_cast<exact_floor_integral_t<U>>(src1_);
4,354,094✔
433
  const exact_floor_integral_t<T> msk = _mask<exact_floor_integral_t<T>, _mask_len::unknown>(len) << start;
4,354,094✔
434
  return src0 ^ ((src0 ^ src1) & msk * (start < digits));
4,354,094✔
435
}
1,198,305✔
436
// -------------------------------------------------------------------------- //
437

438
// ---------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT EXCHANGE ------------ //
439
// Exchanges/swaps bits of src0 by the ones of src1 where the mask is true
440
template <class T>
441
constexpr void _bitexch(T& src0, T& src1, T msk) noexcept {
442
  src0 = src0 ^ static_cast<T>(src1 & msk);
443
  src1 = src1 ^ static_cast<T>(src0 & msk);
444
  src0 = src0 ^ static_cast<T>(src1 & msk);
445
  return;
446
}
447

448
// Replaces len bits of src0 by the ones of src1 starting at start
449
template <class T, class S>
450
constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept {
7,494✔
451
  static_assert(binary_digits<T>::value, "");
2,370✔
452
  constexpr auto digits = binary_digits<T>::value;
7,494✔
453
  const T msk = (len < digits)
7,494!
454
                    ? _mask<T, _mask_len::unknown>(len) << start
7,470✔
455
                    : -1;  // TODO: What if start > 0 here?
2,370✔
456
  src0 = src0 ^ static_cast<T>(src1 & msk);
7,494✔
457
  src1 = src1 ^ static_cast<T>(src0 & msk);
7,494✔
458
  src0 = src0 ^ static_cast<T>(src1 & msk);
7,494✔
459
  return;
7,494✔
460
}
2,370✔
461

462
// Replaces len bits of src0 by the ones of src1 starting at start0
463
// in src0 and start1 in src1.
464
// len <= digits-max(start0, start1)
465
// clang-format off
466
template <class T, class S>
467
constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept
20,307,140✔
468
{
20,594,587✔
469
    static_assert(binary_digits<T>::value, "");
20,594,587✔
470
    const T msk = _mask<T, _mask_len::unknown>(len);
40,901,727✔
471
    if (start0 >= start1) {
40,901,727✔
472
        src0 = src0 ^ (
30,612,099✔
473
                static_cast<T>(src1 << (start0 - start1))
20,455,439✔
474
                &
20,455,439✔
475
                static_cast<T>(msk << start0)
30,612,099✔
476
        );
10,298,779✔
477
        src1 = src1 ^ (
20,455,439✔
478
                static_cast<T>(lsr(src0, (start0 - start1)))
20,455,439✔
479
                &
20,455,439✔
480
                static_cast<T>(msk << start1)
30,612,099✔
481
        );
10,298,779✔
482
        src0 = src0 ^ (
40,768,759✔
483
                static_cast<T>(src1 << (start0 - start1))
20,455,439✔
484
                &
20,455,439✔
485
                static_cast<T>(msk << start0)
30,612,099✔
486
        );
10,298,779✔
487
    } else {
10,298,779✔
488
        src0 = src0 ^ (
20,446,288✔
489
                static_cast<T>(lsr(src1, (start1 - start0)))
20,446,288✔
490
                &
20,446,288✔
491
                static_cast<T>(msk << start0)
30,596,768✔
492
        );
10,295,808✔
493
        src1 = src1 ^ (
30,596,768✔
494
                static_cast<T>(src0 << (start1 - start0))
20,446,288✔
495
                &
20,446,288✔
496
                static_cast<T>(msk << start1)
30,596,768✔
497
        );
10,295,808✔
498
        src0 = src0 ^ (
30,596,768✔
499
                static_cast<T>(lsr(src1, (start1 - start0)))
20,446,288✔
500
                &
20,446,288✔
501
                static_cast<T>(msk << start0)
30,596,768✔
502
        );
10,295,808✔
503
    }
10,295,808✔
504
    return;
40,901,727✔
505
}
20,594,587✔
506
// clang-format on
507
// -------------------------------------------------------------------------- //
508

509
// --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT LEFT ---- //
510
// Left shifts dst by cnt bits, filling the lsbs of dst by the msbs of src
511
template <class T>
512
constexpr T _shld(T dst, T src, T cnt) noexcept {
513
  static_assert(binary_digits<T>::value, "");
514
  constexpr T digits = binary_digits<T>::value;
515
  if (cnt < digits) {
516
    dst = lsl(dst, cnt) | (lsr(src, (digits - cnt)));
517
  } else {
518
    dst = lsl(src, cnt - digits) * (cnt < (digits + digits));
519
  }
520
  return dst;
521
}
522
// -------------------------------------------------------------------------- //
523

524
// --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT RIGHT --- //
525
// Right shifts dst by cnt bits, filling the msbs of dst by the lsbs of src
526
template <class T>
527
constexpr T _shrd(T dst, T src, T cnt) noexcept {
22,279,610✔
528
  static_assert(binary_digits<T>::value, "");
11,838,264✔
529
  constexpr T digits = binary_digits<T>::value;
22,279,610✔
530
  if (cnt < digits) {
22,279,610!
531
    dst = (lsr(dst, cnt)) | lsl(src, (digits - cnt));
22,279,610✔
532
  } else {
11,838,264✔
NEW
533
    dst = (lsr(src, (cnt - digits))) * (cnt < (digits + digits));
×
534
  }
535
  return dst;
22,279,610✔
536
}
11,838,264✔
537
// -------------------------------------------------------------------------- //
538

539
#if defined(NO_X86_INTRINSICS)
540
template <bool Add, std::integral U>
541
static inline unsigned char add_carry_sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept {
542
  static_assert(std::is_unsigned_v<U>, "Only unsigned types are supported");
543

544
  if constexpr (Add) {
545
    U sum1 = a + b;
546
    U sum2 = sum1 + static_cast<U>(c_in);
547
    *out = sum2;
548

549
    // Carry occurs if either sum1 overflows a+b or sum2 overflows sum1+carry
550
    return static_cast<unsigned char>((sum1 < a) || (sum2 < sum1));
551
  } else {
552
    U diff1 = a - b;
553
    U diff2 = diff1 - static_cast<U>(c_in);
554
    *out = diff2;
555

556
    // Borrow occurs if a < b or a - b < carry_in
557
    return static_cast<unsigned char>((a < b) || (diff1 < c_in));
558
  }
559
}
560
#else
561
#if defined(__ADX__)
562
template <bool Add>
563
unsigned char ADDCARRYSUBBORROW32(unsigned char c, uint32_t a, uint32_t b, uint32_t* out) {
564
  return (Add ? _addcarryx_u32(c, a, b, out) : _subborrow_u32(c, a, b, out));
565
}
566
template <bool Add>
567
unsigned char ADDCARRYSUBBORROW64(unsigned char c, uint64_t a, uint64_t b, uint64_t* out) {
568
  static_assert(sizeof(uint64_t) == sizeof(unsigned long long int));
569
  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)));
570
}
571
#else
572
template <bool Add>
573
unsigned char ADDCARRYSUBBORROW32(unsigned char c, uint32_t a, uint32_t b, uint32_t* out) {
4✔
574
  return (Add ? _addcarry_u32(c, a, b, out) : _subborrow_u32(c, a, b, out));
8✔
575
}
2✔
576
template <bool Add>
577
unsigned char ADDCARRYSUBBORROW64(unsigned char c, uint64_t a, uint64_t b, uint64_t* out) {
578
  static_assert(sizeof(uint64_t) == sizeof(unsigned long long int));
579
  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)));
580
}
581
#endif
582

583
template <bool Add, std::integral U>
584
static inline unsigned char add_carry_sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept {
4✔
585
  if constexpr (32 > bitsof<U>()) {
2✔
586
    // a       [aaaaaaaa111111111111111111111111111]
587
    // b     + [bbbbbbbb000000000000000000000000000]
588
    // carry +                            [0000000c]
589
    const uint8_t shift = (32 - bitsof<U>());
4✔
590
    uint32_t carry_propagation = Add ? ((1 << shift) - 1) : 0;
4✔
591
    uint32_t tmp_out;
4✔
592
    unsigned char carry = ADDCARRYSUBBORROW32<Add>(
6✔
593
        c_in,
2✔
594
        (static_cast<uint32_t>(a) << shift) | carry_propagation,
4✔
595
        (static_cast<uint32_t>(b) << shift),
4✔
596
        &tmp_out);
2✔
597
    *out = static_cast<U>(tmp_out >> shift);
4✔
598
    return carry;
4✔
599
  } else if constexpr (32 == bitsof<U>()) {
600
    return ADDCARRYSUBBORROW32<Add>(c_in, static_cast<uint32_t>(a), static_cast<uint32_t>(b), reinterpret_cast<uint32_t>(out));
601
  } else if constexpr (64 == bitsof<U>()) {
602
    return ADDCARRYSUBBORROW64<Add>(c_in, static_cast<uint64_t>(a), static_cast<uint64_t>(b), reinterpret_cast<uint64_t>(out));
603
  } else if constexpr (0 == (bitsof<U>() % 64)) {
604
    using t64 = std::conditional<std::is_signed_v<U>, int64_t, uint64_t>;
605
    unsigned char carry;
606
    for (int i = 0; i < (bitsof<U>() / 64); i++) {
607
      carry = ADDCARRYSUBBORROW64<Add>(c_in, static_cast<t64>(a >> (i * 64)), static_cast<t64>(b >> (i * 64)), reinterpret_cast<t64>(out) + i);
608
    }
609
    return carry;
610
  } else {
611
    assert(((void)"add carry intrinsics support only support powers of 2 bits", false));
612
  }
613
}
2✔
614
#endif  // NO_X86_INTRINSICS
615

616
template <std::integral U>
617
static inline unsigned char add_carry(unsigned char c_in, U a, U b, U* out) noexcept {
4✔
618
  return add_carry_sub_borrow<true, U>(c_in, a, b, out);
4✔
619
}
2✔
620

621
template <std::integral U>
622
static inline unsigned char sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept {
623
  return add_carry_sub_borrow<false, U>(c_in, a, b, out);
624
}
625

626
// -------------------------------------------------------------------------- //
627

628
template <typename T, typename T128>
629
constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder) noexcept {
6✔
630
  constexpr auto digits = bitsof<T>();
6✔
631
  if constexpr (digits > bitsof<uint32_t>()) {
632
    T128 tmp128 = (static_cast<T128>(numerator_hi) << digits) | static_cast<T128>(numerator_lo);
633
    T quotient = static_cast<T>(tmp128 / static_cast<T128>(denominator));
634
    if (remainder) {
635
      *remainder = static_cast<T>(tmp128 % static_cast<T128>(denominator));
636
    }
637
    return quotient;
638
  } else {
3✔
639
    return _divx(numerator_hi, numerator_lo, denominator, remainder, std::ignore);
6✔
640
  }
3✔
641
}
3✔
642

643
template <typename T, typename... X>
644
constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder, X...) noexcept {
6✔
645
  constexpr auto digits = bitsof<T>();
6✔
646
  using wider_t = ceil_integral<digits + digits>;
3✔
647
  if constexpr ((digits + digits) <= binary_digits<wider_t>::value) {
3✔
648
    wider_t tmp = (static_cast<wider_t>(numerator_hi) << digits) | static_cast<wider_t>(numerator_lo);
6✔
649
    T quotient = static_cast<T>(tmp / static_cast<wider_t>(denominator));
6✔
650
    if (remainder) {
6!
651
      *remainder = static_cast<T>(tmp % static_cast<wider_t>(denominator));
6✔
652
    }
3✔
653
    return quotient;
6✔
654
  } else if constexpr (digits > bitsof<uint32_t>()) {
655
    const T& numhi = numerator_hi;
656
    const T& numlo = numerator_lo;
657
    const T& den = denominator;
658
    // We work in base 2**32.
659
    // A uint32 holds a single digit. A uint64 holds two digits.
660
    // Our numerator is conceptually [num3, num2, num1, num0].
661
    // Our denominator is [den1, den0].
662
    const uint64_t b = (1ull << 32);
663

664
    // The high and low digits of our computed quotient.
665
    uint32_t q1;
666
    uint32_t q0;
667

668
    // The normalization shift factor.
669
    int shift;
670

671
    // The high and low digits of our denominator (after normalizing).
672
    // Also the low 2 digits of our numerator (after normalizing).
673
    uint32_t den1;
674
    uint32_t den0;
675
    uint32_t num1;
676
    uint32_t num0;
677

678
    // A partial remainder.
679
    uint64_t rem;
680

681
    // The estimated quotient, and its corresponding remainder (unrelated to true remainder).
682
    uint64_t qhat;
683
    uint64_t rhat;
684

685
    // Variables used to correct the estimated quotient.
686
    uint64_t c1;
687
    uint64_t c2;
688

689
    // Check for overflow and divide by 0.
690
    if (numhi >= den) {
691
      if (remainder != nullptr) {
692
        *remainder = ~0ull;
693
      }
694
      return ~0ull;
695
    }
696

697
    // Determine the normalization factor. We multiply den by this, so that its leading digit is at
698
    // least half b. In binary this means just shifting left by the number of leading zeros, so that
699
    // there's a 1 in the MSB.
700
    // We also shift numer by the same amount. This cannot overflow because numhi < den.
701
    // The expression (-shift & 63) is the same as (64 - shift), except it avoids the UB of shifting
702
    // by 64. The funny bitwise 'and' ensures that numlo does not get shifted into numhi if shift is 0.
703
    // clang 11 has an x86 codegen bug here: see LLVM bug 50118. The sequence below avoids it.
704
    shift = __builtin_clzll(den);
705
    den <<= shift;
706
    numhi <<= shift;
707
    numhi |= (numlo >> (-shift & 63)) & (-(int64_t)shift >> 63);
708
    numlo <<= shift;
709

710
    // Extract the low digits of the numerator and both digits of the denominator.
711
    num1 = (uint32_t)(numlo >> 32);
712
    num0 = (uint32_t)(numlo & 0xFFFFFFFFu);
713
    den1 = (uint32_t)(den >> 32);
714
    den0 = (uint32_t)(den & 0xFFFFFFFFu);
715

716
    // We wish to compute q1 = [n3 n2 n1] / [d1 d0].
717
    // Estimate q1 as [n3 n2] / [d1], and then correct it.
718
    // Note while qhat may be 2 digits, q1 is always 1 digit.
719
    qhat = numhi / den1;
720
    rhat = numhi % den1;
721
    c1 = qhat * den0;
722
    c2 = rhat * b + num1;
723
    if (c1 > c2) {
724
      qhat -= (c1 - c2 > den) ? 2 : 1;
725
    }
726
    q1 = (uint32_t)qhat;
727

728
    // Compute the true (partial) remainder.
729
    rem = numhi * b + num1 - q1 * den;
730

731
    // We wish to compute q0 = [rem1 rem0 n0] / [d1 d0].
732
    // Estimate q0 as [rem1 rem0] / [d1] and correct it.
733
    qhat = rem / den1;
734
    rhat = rem % den1;
735
    c1 = qhat * den0;
736
    c2 = rhat * b + num0;
737
    if (c1 > c2) {
738
      qhat -= (c1 - c2 > den) ? 2 : 1;
739
    }
740
    q0 = (uint32_t)qhat;
741

742
    // Return remainder if requested.
743
    if (remainder != nullptr) {
744
      *remainder = (rem * b + num0 - q0 * den) >> shift;
745
    }
746
    return ((uint64_t)q1 << 32) | q0;
747
  } else {
748
    assert(false);
749
  }
750
}
3✔
751

752
// -------- IMPLEMENTATION DETAILS: INSTRUCTIONS: MULTIWORD MULTIPLY -------- //
753
// Multiplies src0 and src1 and gets the full result with compiler intrinsics
754
template <typename T, typename T128>
755
constexpr T _mulx(T src0, T src1, T* hi) noexcept {
6✔
756
  using wider_t = ceil_integral<bitsof<T>() + bitsof<T>()>;
3✔
757
  constexpr auto digits = bitsof<T>();
6✔
758
  if constexpr (digits > bitsof<uint32_t>()) {
759
    T128 tmp128 = static_cast<T128>(src0) * static_cast<T128>(src1);
760
    *hi = static_cast<T>(tmp128 >> digits);
761
    return static_cast<T>(tmp128);
762
  } else {
3✔
763
    return _mulx(src0, src1, hi, std::ignore);
6✔
764
  }
3✔
765
}
3✔
766

767
template <typename T, typename... X>
768
constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept {
6✔
769
  constexpr T digits = bitsof<T>();
6✔
770
  using wider_t = ceil_integral<bitsof<T>() + bitsof<T>()>;
3✔
771
  if constexpr ((digits + digits) <= bitsof<wider_t>()) {
3✔
772
    wider_t tmp = static_cast<wider_t>(src0) * static_cast<wider_t>(src1);
6✔
773
    *hi = tmp >> digits;
6✔
774
    return static_cast<T>(tmp);
6✔
775
  } else {
776
    // Multiplies src0 and src1 and gets the full result without compiler intrinsics
777
    constexpr T offset = digits / 2;
778
    const T lsbs0 = src0 & _mask<T>(digits - offset);
779
    const T msbs0 = lsr(src0, offset);
780
    const T lsbs1 = src1 & _mask<T>(digits - offset);
781
    const T msbs1 = lsr(src1, offset);
782
    const T llsbs = lsbs0 * lsbs1;
783
    const T mlsbs = msbs0 * lsbs1;
784
    const T lmsbs = lsbs0 * msbs1;
785
    const T mi = mlsbs + lmsbs;
786
    const T lo = llsbs + static_cast<T>(mi << offset);
787
    const T lcarry = lo < llsbs || lo < static_cast<T>(mi << offset);
788
    const T mcarry = static_cast<T>(mi < mlsbs || mi < lmsbs) << offset;
789
    *hi = static_cast<T>(lsr(mi, offset)) + msbs0 * msbs1 + mcarry + lcarry;
790
    return lo;
791
  }
792
}
3✔
793
// -------------------------------------------------------------------------- //
794

795
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
796
constexpr auto with_bit_iterator_adapter(
21,446✔
797
    const bit_iterator<SrcIt>& first,
798
    const bit_iterator<SrcIt>& last,
799
    const bit_iterator<DstIt>& d_first) {
21,446✔
800
  using dst_word_type = typename bit_iterator<DstIt>::word_type;
21,446✔
801
  using src_word_type = typename bit_iterator<SrcIt>::word_type;
21,446✔
802
  if constexpr (!std::is_same_v<src_word_type, dst_word_type> && bitsof<src_word_type>() != bitsof<dst_word_type>()) {
21,446✔
803
    if constexpr (bitsof<src_word_type>() > bitsof<dst_word_type>()) {
9,052✔
804
      bit_iterator<bit_word_pointer_adapter<DstIt, SrcIt>> adapted_first(
18,100✔
805
          bit_word_pointer_adapter<DstIt, SrcIt>(first.base(), first.position() / bitsof<dst_word_type>()), first.position() % bitsof<dst_word_type>());
13,575✔
806
      bit_iterator<bit_word_pointer_adapter<DstIt, SrcIt>> adapted_last(
18,100✔
807
          bit_word_pointer_adapter<DstIt, SrcIt>(last.base(), last.position() / bitsof<dst_word_type>()), last.position() % bitsof<dst_word_type>());
13,575✔
808
      return AlgoFunc{}(adapted_first, adapted_last, d_first);
13,575✔
809
    } else {
4,527✔
810
      bit_iterator<bit_word_pointer_adapter<SrcIt, DstIt>> adapted_d_first(
18,108✔
811
          bit_word_pointer_adapter<SrcIt, DstIt>(d_first.base(), d_first.position() / bitsof<src_word_type>()), d_first.position() % bitsof<src_word_type>());
13,581✔
812
      return AlgoFunc{}(first, last, adapted_d_first);
13,581✔
813
    }
4,527✔
814
  } else {
12,394✔
815
    return AlgoFunc{}(first, last, d_first);
37,182✔
816
  }
12,394✔
817
}
21,446✔
818

819
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
820
constexpr auto with_bit_iterator_adapter(
23✔
821
    const SrcIt& first,
822
    const SrcIt& last,
823
    const bit_iterator<DstIt>& d_first) {
23✔
824
  return with_bit_iterator_adapter<AlgoFunc>(bit_iterator(first), bit_iterator(last), d_first);
69✔
825
}
23✔
826

827
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
828
constexpr auto with_bit_iterator_adapter(
8✔
829
    const bit_iterator<SrcIt>& first,
830
    const bit_iterator<SrcIt>& last,
831
    const DstIt& d_first) {
8✔
832
  return with_bit_iterator_adapter<AlgoFunc>(first, last, bit_iterator(d_first));
24✔
833
}
8✔
834

835
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
836
constexpr auto with_bit_iterator_adapter(
837
    const bit_iterator<SrcIt>& first,
838
    const bit_iterator<DstIt>& last) {
839
  using dst_word_type = typename bit_iterator<DstIt>::word_type;
840
  using src_word_type = typename bit_iterator<SrcIt>::word_type;
841
  if constexpr (!std::is_same_v<src_word_type, dst_word_type> && bitsof<src_word_type>() != bitsof<dst_word_type>()) {
842
    if constexpr (bitsof<src_word_type>() > bitsof<dst_word_type>()) {
843
      bit_iterator<bit_word_pointer_adapter<DstIt, SrcIt>> adapted_first(
844
          bit_word_pointer_adapter<DstIt, SrcIt>(first.base(), first.position() / bitsof<dst_word_type>()), first.position() % bitsof<dst_word_type>());
845
      return AlgoFunc{}(adapted_first, last);
846
    } else {
847
      bit_iterator<bit_word_pointer_adapter<SrcIt, DstIt>> adapted_last(
848
          bit_word_pointer_adapter<SrcIt, DstIt>(last.base(), last.position() / bitsof<src_word_type>()), last.position() % bitsof<src_word_type>());
849
      return AlgoFunc{}(first, adapted_last);
850
    }
851
  } else {
852
    return AlgoFunc{}(first, last);
853
  }
854
}
855

856
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
857
constexpr auto with_bit_iterator_adapter(
858
    const SrcIt& first,
859
    const bit_iterator<DstIt>& last) {
860
  return with_bit_iterator_adapter<AlgoFunc>(bit_iterator(first), last);
861
}
862

863
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
864
constexpr auto with_bit_iterator_adapter(
865
    const bit_iterator<SrcIt>& first,
866
    const DstIt& last) {
867
  return with_bit_iterator_adapter<AlgoFunc>(first, bit_iterator(last));
868
}
869

870
namespace detail {
871

872
struct uninitialized_t {
873
  explicit uninitialized_t() = default;
874
};
875
inline constexpr uninitialized_t uninitialized{};
876

877
template <typename size_type, bool resizeable, std::size_t Extent>
878
struct container_size_storage {
879
  constexpr size_type size() const noexcept {
286✔
880
    return Extent;
286✔
881
  }
143✔
882

883
  constexpr container_size_storage() noexcept {}
285✔
884
};
885

886
template <typename size_type, bool resizeable>
887
struct container_size_storage<size_type, resizeable, std::dynamic_extent> {
888
  using maybe_const_size_type = std::conditional_t<resizeable, size_type, std::add_const_t<size_type>>;
889

890
  maybe_const_size_type size_;
891
  constexpr size_type size() const noexcept {
3,930✔
892
    return size_;
3,930✔
893
  }
1,965✔
894
  constexpr void resize(const size_type& new_size)
895
    requires(resizeable)
896
  {
897
    size_ = new_size;
898
  }
899

900
  constexpr container_size_storage() noexcept : size_() {}
901
  constexpr container_size_storage(const size_type& size) noexcept
123✔
902
      : size_(size) {}
247✔
903
};
904

905
}  // namespace detail
906

907
// ========================================================================== //
908
}  // namespace bit
909
#endif // _BIT_DETAILS_HPP_INCLUDED
910
       // ========================================================================== //
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