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

PeterCDMcLean / BitLib / 16544577083

26 Jul 2025 10:39PM UTC coverage: 76.997% (-1.5%) from 78.485%
16544577083

Pull #18

github

web-flow
Merge 4bac8f49d into 079daa142
Pull Request #18: From string

3414 of 5100 branches covered (66.94%)

Branch coverage included in aggregate %.

264 of 313 new or added lines in 12 files covered. (84.35%)

30 existing lines in 2 files now uncovered.

2494 of 2573 relevant lines covered (96.93%)

31115123.0 hits per line

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

71.18
/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
// clang-format off
19
#if defined(_MSC_VER)
20
  #if __has_include(<intrin.h>)
21
    #include <intrin.h>
22
  #else
23
    #define NO_X86_INTRINSICS
24
  #endif
25
#else
26
  #if __has_include(<x86intrin.h>)
27
      #include <x86intrin.h>
28
  #else
29
    #if __has_include(<immintrin.h>)
30
      #include <immintrin.h>
31
    #else
32
      #define NO_X86_INTRINSICS
33
    #endif
34
  #endif
35
#endif  // defined(_MSC_VER)
36
// clang-format on
37

38
#include <algorithm>
39
#include <bit>
40
#include <cassert>
41
#include <concepts>
42
#include <cstddef>
43
#include <cstdint>
44
#include <iterator>
45
#include <limits>
46
#include <ranges>
47
#include <stdexcept>
48
#include <tuple>
49
#include <type_traits>
50
#include <utility>
51

52
#include "bitlib/bit-containers/bit_bitsof.hpp"
53
#include "bitlib/bit_concepts.hpp"
54

55
// Project sources
56
// Third-party libraries
57
// Miscellaneous
58
namespace bit {
59
class bit_value;
60
template <class WordType>
61
class bit_reference;
62
template <class Iterator>
63
class bit_iterator;
64
template <class WordType>
65
using bit_pointer = bit_iterator<WordType*>;
66
template <typename target_word_ptr, typename source_word_ptr>
67
class bit_word_pointer_adapter;
68

69
// ========================================================================== //
70

71
template <size_t N>
72
using ceil_integral = std::conditional_t<
73
    (N <= bitsof<std::uint8_t>()),
74
    std::uint8_t,
75
    std::conditional_t<
76
        (N <= bitsof<std::uint16_t>()),
77
        std::uint16_t,
78
        std::conditional_t<
79
            (N <= bitsof<std::uint32_t>()),
80
            std::uint32_t,
81
            std::conditional_t<
82
                (N <= bitsof<std::uint64_t>()),
83
                std::uint64_t,
84
                std::uint64_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

106
    //    static_assert(std::is_same<_raw_pointer_t, _raw_value_t>::value, "");
107
    //    static_assert(std::is_same<_raw_reference_t, _raw_value_t>::value, "");
108

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

119
/*
120
exact_floor_integral is used to determine the exact integral type that a proxy reference
121
can be implicitly converted to.
122
If the proxy reference supports multiple types, it will pick the smallest, preferring unsigned.
123
*/
124
template <typename T>
125
struct exact_floor_integral {
126
 private:
127
  using U = std::remove_cvref_t<T>;
128

129
  template <typename From, typename To>
130
  static constexpr bool is_exactly_convertible() {
131
    if constexpr (!std::is_convertible_v<From, To>) {
132
      return false;
133
    } else {
134
      // Try brace-initialization to detect narrowing
135
      return requires(From f) {
136
        To{f};  // will fail if narrowing
137
      };
138
    }
139
  }
140

141
 public:
142
  using type =
143
      std::conditional_t<
144
          is_exactly_convertible<U, uint8_t>(), uint8_t,
145
          std::conditional_t<
146
              is_exactly_convertible<U, int8_t>(), int8_t,
147
              std::conditional_t<
148
                  is_exactly_convertible<U, uint16_t>(), uint16_t,
149
                  std::conditional_t<
150
                      is_exactly_convertible<U, int16_t>(), int16_t,
151
                      std::conditional_t<
152
                          is_exactly_convertible<U, uint32_t>(), uint32_t,
153
                          std::conditional_t<
154
                              is_exactly_convertible<U, int32_t>(), int32_t,
155
                              std::conditional_t<
156
                                  is_exactly_convertible<U, uint64_t>(), uint64_t,
157
                                  std::conditional_t<
158
                                      is_exactly_convertible<U, int64_t>(), int64_t,
159
                                      void>>>>>>>>;
160
};
161

162
// Helper alias
163
template <typename T>
164
using exact_floor_integral_t = typename exact_floor_integral<T>::type;
165

166
template <typename From, typename To, typename = void>
167
struct is_static_castable : std::false_type {};
168

169
template <typename From, typename To>
170
struct is_static_castable<From, To, std::void_t<decltype(static_cast<To>(std::declval<From>()))>>
171
    : std::true_type {};
172

173
template <typename From, typename To>
174
constexpr bool is_static_castable_v = is_static_castable<From, To>::value;
175

176
/* ***************************** BINARY DIGITS ****************************** */
177
// Binary digits structure definition
178
// Implementation template: only instantiates static_asserts for non-byte types.
179
template <typename T, bool = std::is_same<T, std::byte>::value>
180
struct binary_digits_impl : std::integral_constant<std::size_t, std::numeric_limits<std::make_unsigned_t<exact_floor_integral_t<T>>>::digits> {
181
  static_assert(std::is_integral<exact_floor_integral_t<T>>::value, "Type must be integral");
182
  //static_assert(std::is_unsigned<T>::value, "Type must be unsigned");
183
  static_assert(!std::is_same<exact_floor_integral_t<T>, bool>::value, "Type must not be bool");
184
  static_assert(!std::is_same<exact_floor_integral_t<T>, char>::value, "Type must not be char");
185
};
186

187
// Specialization for std::byte.
188
template <>
189
struct binary_digits_impl<std::byte, true> : std::integral_constant<std::size_t, std::numeric_limits<unsigned char>::digits> {};
190

191
// Public interface that removes cv-qualifiers.
192
template <typename UIntType>
193
struct binary_digits : binary_digits_impl<std::remove_cv_t<UIntType>> {};
194

195
// Binary digits value
196
template <class T>
197
constexpr std::size_t binary_digits_v = binary_digits<T>::value;
198
/*************************************************************************** */
199

200
/* ******************* IMPLEMENTATION DETAILS: UTILITIES ******************** */
201
// Assertions
202
template <class Iterator>
203
constexpr bool _assert_range_viability(Iterator first, Iterator last);
204
/* ************************************************************************** */
205

206
// Bit field extraction
207
template <class T, class = decltype(__builtin_ia32_bextr_u64(T(), T(), T()))>
208
constexpr T _bextr(T src, T start, T len) noexcept;
209
template <class T, class... X>
210
constexpr T _bextr(T src, T start, T len, X...) noexcept;
211

212
// Bit swap
213
template <class T>
214
constexpr T _bitswap(T src) noexcept;
215
template <class T, std::size_t N>
216
constexpr T _bitswap(T src) noexcept;
217
template <class T, std::size_t N>
218
constexpr T _bitswap() noexcept;
219

220
// Bit exchange
221
template <class T>
222
constexpr void _bitexch(T& src0, T& src1, T msk) noexcept;
223
template <class T, class S>
224
constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept;
225
template <class T, class S>
226
constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept;
227

228
// Double precision shift left
229
template <class T>
230
constexpr T _shld(T dst, T src, T cnt) noexcept;
231

232
// Double precision shift right
233
template <class T>
234
constexpr T _shrd(T dst, T src, T cnt) noexcept;
235

236
// Multiword multiply
237
template <typename T, typename T128 = decltype(__uint128_t(T()))>
238
constexpr T _mulx(T src0, T src1, T* hi) noexcept;
239
template <typename T, typename... X>
240
constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept;
241
template <typename T, typename T128 = decltype(__uint128_t(T()))>
242
constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder) noexcept;
243
template <typename T, typename... X>
244
constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder, X...) noexcept;
245
/* ************************************************************************** */
246

247
/*
248
Logical shift right
249
*/
250
template <std::integral T, typename size_type = size_t>
251
constexpr T lsr(const T val, const size_type shift) {
172,289,203✔
252
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
86,079,181✔
253
  assert(shift < bitsof<T>());
172,289,203!
254
#endif
86,079,181✔
255
  return static_cast<T>(static_cast<std::make_unsigned_t<T>>(val) >> shift);
172,289,203✔
256
}
86,079,181✔
257

258
/*
259
Logic shift right when `val` operand is a proxy reference
260
*/
261
template <typename T, typename size_type = size_t>
262
constexpr exact_floor_integral_t<T> lsr(const T val, const size_type shift) {
263
  static_assert(!std::is_same_v<exact_floor_integral_t<T>, void>,
264
                "Type T must be convertible to an integral type");
265
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
266
  assert(shift < bitsof<exact_floor_integral_t<T>>());
267
#endif
268
  return static_cast<exact_floor_integral_t<T>>(static_cast<std::make_unsigned_t<exact_floor_integral_t<T>>>(val) >> shift);
269
}
270

271
/*
272
Logical shift left
273
*/
274
template <std::integral T, typename size_type = size_t>
275
constexpr T lsl(const T val, const size_type shift) {
22,279,610✔
276
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
11,838,264✔
277
  assert(shift < bitsof<T>());
22,279,610!
278
#endif
11,838,264✔
279
  return static_cast<T>(static_cast<std::make_unsigned_t<T>>(val) << shift);
22,279,610✔
280
}
11,838,264✔
281

282
/*
283
Logic shift left when `val` operand is a proxy reference
284
*/
285
template <typename T, typename size_type = size_t>
286
constexpr exact_floor_integral_t<T> lsl(const T val, const size_type shift) {
287
  static_assert(!std::is_same_v<exact_floor_integral_t<T>, void>,
288
                "Type T must be convertible to an integral type");
289
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
290
  assert(shift < bitsof<exact_floor_integral_t<T>>());
291
#endif
292
  return static_cast<exact_floor_integral_t<T>>(static_cast<std::make_unsigned_t<exact_floor_integral_t<T>>>(val) << shift);
293
}
294

295
enum class _mask_len {
296
  unknown,
297
  in_range
298
};
299

300
template <std::integral T, _mask_len len_in_range = _mask_len::in_range, typename size_type = size_t>
301
constexpr T _mask(const size_type len) {
45,321,767✔
302
  constexpr std::make_unsigned_t<T> one = std::make_unsigned_t<T>(1);
45,321,767✔
303
  if constexpr (len_in_range != _mask_len::unknown) {
21,824,257✔
304
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
29,009✔
305
    assert(len < bitsof<T>());
58,419!
306
#endif
29,009✔
307
    return static_cast<T>((one << len) - one);
58,419✔
308
  } else {
21,795,248✔
309
    // The digits_mask is solely here to prevent Undefined Sanitizer
310
    // complaining about shift of len >= digits
311
    // Note: on -O1 the (len & digits_mask) is optimized to simply (len)
312
    constexpr std::make_unsigned_t<T> digits_mask = bitsof<T>() - one;
45,263,348✔
313
    return static_cast<T>((one << (len & digits_mask)) * (len < bitsof<T>()) - one);
45,263,348✔
314
  }
21,795,248✔
315
}
21,824,257✔
316

317
// ------------- IMPLEMENTATION DETAILS: UTILITIES: ASSERTIONS -------------- //
318
// If the range allows multipass iteration, checks if last - first >= 0
319
template <class Iterator>
320
constexpr bool _assert_range_viability(Iterator first, Iterator last) {
1,027,042✔
321
  using traits_t = std::iterator_traits<Iterator>;
509,588✔
322
  using category_t = typename traits_t::iterator_category;
509,588✔
323
  using multi_t = std::forward_iterator_tag;
509,588✔
324
  constexpr bool is_multipass = std::is_base_of<multi_t, category_t>::value;
1,027,042✔
325
  const bool is_viable = !is_multipass || std::distance(first, last) >= 0;
1,544,496!
326
  assert(is_viable);
1,027,042!
327
  return is_viable;
1,544,496✔
328
}
509,588✔
329
// -------------------------------------------------------------------------- //
330

331
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT FIELD EXTRACTION ------- //
332
// Extacts to lsbs a field of contiguous bits with compiler intrinsics
333
template <class T, class>
334
constexpr T _bextr(T src, T start, T len) noexcept {
335
  static_assert(binary_digits<T>::value, "");
336
  constexpr T digits = binary_digits<T>::value;
337
  T dst = T();
338
  if (digits <= std::numeric_limits<unsigned int>::digits) {
339
    dst = __builtin_ia32_bextr_u32(src, start, len);
340
  } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
341
    dst = __builtin_ia32_bextr_u64(src, start, len);
342
  } else {
343
    dst = _bextr(src, start, len, std::ignore);
344
  }
345
  return dst;
346
}
347

348
// Extacts to lsbs a field of contiguous bits without compiler intrinsics
349
template <class T, class... X>
350
constexpr T _bextr(T src, T start, T len, X...) noexcept {
15,110✔
351
  static_assert(binary_digits<T>::value, "");
7,619✔
352
  constexpr T digits = binary_digits<T>::value;
15,110✔
353
  constexpr T one = 1;
15,110✔
354
  const T msk = (one << len) * (len < digits) - one;
15,110✔
355
  return (lsr(src, start)) & msk * (start < digits);
15,110✔
356
}
7,619✔
357
// -------------------------------------------------------------------------- //
358

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

392
// Reverses the order of the bits: recursive metafunction
393
template <class T, std::size_t N>
394
constexpr T _bitswap(T src) noexcept {
79,020,032✔
395
  static_assert(binary_digits<T>::value, "");
39,511,840✔
396
  constexpr T cnt = N >> 1;
79,020,032✔
397
  constexpr T msk = _bitswap<T, cnt>();
79,020,032✔
398
  src = ((lsr(src, cnt)) & msk) | ((src << cnt) & ~msk);
79,020,032✔
399
  return cnt > 1 ? _bitswap<T, cnt>(src) : src;
79,020,032✔
400
}
39,511,840✔
401

402
// Reverses the order of the bits: mask for the recursive metafunction
403
template <class T, std::size_t N>
404
constexpr T _bitswap() noexcept {
405
  static_assert(binary_digits<T>::value, "");
406
  constexpr T digits = binary_digits<T>::value;
407
  T cnt = digits;
408
  T msk = ~T();
409
  while (cnt != N) {
410
    cnt = lsr(cnt, 1);
411
    msk ^= (msk << cnt);
412
  }
413
  return msk;
414
}
415
// -------------------------------------------------------------------------- //
416

417
// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT BLEND ------------- //
418
// Replaces bits of src0 by the ones of src1 where the mask is true
419

420
template <typename T, typename U>
421
constexpr exact_floor_integral_t<T> _bitblend(
11,690✔
422
    const T src0_,
423
    const U src1_,
424
    const exact_floor_integral_t<T> msk) noexcept
425
  requires(std::is_same_v<exact_floor_integral_t<T>, exact_floor_integral_t<U>>)
426
{
11,496✔
427
  static_assert(binary_digits<exact_floor_integral_t<T>>::value, "");
11,496✔
428
  const exact_floor_integral_t<T> src0 = static_cast<exact_floor_integral_t<T>>(src0_);
23,186✔
429
  const exact_floor_integral_t<U> src1 = static_cast<exact_floor_integral_t<U>>(src1_);
23,186✔
430
  return src0 ^ ((src0 ^ src1) & msk);
23,186✔
431
}
11,496✔
432

433
// Replaces len bits of src0 by the ones of src1 starting at start
434
template <typename T, typename U>
435
constexpr exact_floor_integral_t<T> _bitblend(
3,155,860✔
436
    const T src0_,
437
    const U src1_,
438
    const exact_floor_integral_t<T> start,
439
    const exact_floor_integral_t<T> len) noexcept
440
  requires(std::is_same_v<exact_floor_integral_t<T>, exact_floor_integral_t<U>>)
441
{
1,198,404✔
442
  static_assert(binary_digits<exact_floor_integral_t<T>>::value, "");
1,198,404✔
443
  constexpr exact_floor_integral_t<T> digits = bitsof<exact_floor_integral_t<T>>();
4,354,264✔
444
  const exact_floor_integral_t<T> src0 = static_cast<exact_floor_integral_t<T>>(src0_);
4,354,264✔
445
  const exact_floor_integral_t<U> src1 = static_cast<exact_floor_integral_t<U>>(src1_);
4,354,264✔
446
  const exact_floor_integral_t<T> msk = _mask<exact_floor_integral_t<T>, _mask_len::unknown>(len) << start;
4,354,264✔
447
  return src0 ^ ((src0 ^ src1) & msk * (start < digits));
4,354,264✔
448
}
1,198,404✔
449
// -------------------------------------------------------------------------- //
450

451
// ---------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT EXCHANGE ------------ //
452
// Exchanges/swaps bits of src0 by the ones of src1 where the mask is true
453
template <class T>
454
constexpr void _bitexch(T& src0, T& src1, T msk) noexcept {
455
  src0 = src0 ^ static_cast<T>(src1 & msk);
456
  src1 = src1 ^ static_cast<T>(src0 & msk);
457
  src0 = src0 ^ static_cast<T>(src1 & msk);
458
  return;
459
}
460

461
// Replaces len bits of src0 by the ones of src1 starting at start
462
template <class T, class S>
463
constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept {
7,494✔
464
  static_assert(binary_digits<T>::value, "");
2,370✔
465
  constexpr auto digits = binary_digits<T>::value;
7,494✔
466
  const T msk = (len < digits)
7,494!
467
                    ? _mask<T, _mask_len::unknown>(len) << start
7,470✔
468
                    : -1;  // TODO: What if start > 0 here?
2,370✔
469
  src0 = src0 ^ static_cast<T>(src1 & msk);
7,494✔
470
  src1 = src1 ^ static_cast<T>(src0 & msk);
7,494✔
471
  src0 = src0 ^ static_cast<T>(src1 & msk);
7,494✔
472
  return;
7,494✔
473
}
2,370✔
474

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

522
// --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT LEFT ---- //
523
// Left shifts dst by cnt bits, filling the lsbs of dst by the msbs of src
524
template <class T>
525
constexpr T _shld(T dst, T src, T cnt) noexcept {
526
  static_assert(binary_digits<T>::value, "");
527
  constexpr T digits = binary_digits<T>::value;
528
  if (cnt < digits) {
529
    dst = lsl(dst, cnt) | (lsr(src, (digits - cnt)));
530
  } else {
531
    dst = lsl(src, cnt - digits) * (cnt < (digits + digits));
532
  }
533
  return dst;
534
}
535
// -------------------------------------------------------------------------- //
536

537
// --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT RIGHT --- //
538
// Right shifts dst by cnt bits, filling the msbs of dst by the lsbs of src
539
template <class T>
540
constexpr T _shrd(T dst, T src, T cnt) noexcept {
22,279,610✔
541
  static_assert(binary_digits<T>::value, "");
11,838,264✔
542
  constexpr T digits = binary_digits<T>::value;
22,279,610✔
543
  if (cnt < digits) {
22,279,610!
544
    dst = (lsr(dst, cnt)) | lsl(src, (digits - cnt));
22,279,610✔
545
  } else {
11,838,264✔
NEW
546
    dst = (lsr(src, (cnt - digits))) * (cnt < (digits + digits));
×
547
  }
548
  return dst;
22,279,610✔
549
}
11,838,264✔
550
// -------------------------------------------------------------------------- //
551

552
#if defined(NO_X86_INTRINSICS)
553
template <bool Add, std::integral U>
554
static inline unsigned char add_carry_sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept {
555
  static_assert(std::is_unsigned_v<U>, "Only unsigned types are supported");
556

557
  if constexpr (Add) {
558
    U sum1 = a + b;
559
    U sum2 = sum1 + static_cast<U>(c_in);
560
    *out = sum2;
561

562
    // Carry occurs if either sum1 overflows a+b or sum2 overflows sum1+carry
563
    return static_cast<unsigned char>((sum1 < a) || (sum2 < sum1));
564
  } else {
565
    U diff1 = a - b;
566
    U diff2 = diff1 - static_cast<U>(c_in);
567
    *out = diff2;
568

569
    // Borrow occurs if a < b or a - b < carry_in
570
    return static_cast<unsigned char>((a < b) || (diff1 < c_in));
571
  }
572
}
573
#else
574
#if defined(__ADX__)
575
template <bool Add>
576
unsigned char ADDCARRYSUBBORROW32(unsigned char c, uint32_t a, uint32_t b, uint32_t* out) {
577
  return (Add ? _addcarryx_u32(c, a, b, out) : _subborrow_u32(c, a, b, out));
578
}
579
template <bool Add>
580
unsigned char ADDCARRYSUBBORROW64(unsigned char c, uint64_t a, uint64_t b, uint64_t* out) {
581
  static_assert(sizeof(uint64_t) == sizeof(unsigned long long int));
582
  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)));
583
}
584
#else
585
template <bool Add>
586
unsigned char ADDCARRYSUBBORROW32(unsigned char c, uint32_t a, uint32_t b, uint32_t* out) {
10✔
587
  return (Add ? _addcarry_u32(c, a, b, out) : _subborrow_u32(c, a, b, out));
20✔
588
}
5✔
589
template <bool Add>
590
unsigned char ADDCARRYSUBBORROW64(unsigned char c, uint64_t a, uint64_t b, uint64_t* out) {
591
  static_assert(sizeof(uint64_t) == sizeof(unsigned long long int));
592
  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)));
593
}
594
#endif
595

596
template <bool Add, std::integral U>
597
static inline unsigned char add_carry_sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept {
10✔
598
  if constexpr (32 > bitsof<U>()) {
5✔
599
    // a       [aaaaaaaa111111111111111111111111111]
600
    // b     + [bbbbbbbb000000000000000000000000000]
601
    // carry +                            [0000000c]
602
    const uint8_t shift = (32 - bitsof<U>());
10✔
603
    uint32_t carry_propagation = Add ? ((1 << shift) - 1) : 0;
10✔
604
    uint32_t tmp_out;
10✔
605
    unsigned char carry = ADDCARRYSUBBORROW32<Add>(
15✔
606
        c_in,
5✔
607
        (static_cast<uint32_t>(a) << shift) | carry_propagation,
10✔
608
        (static_cast<uint32_t>(b) << shift),
10✔
609
        &tmp_out);
5✔
610
    *out = static_cast<U>(tmp_out >> shift);
10✔
611
    return carry;
10✔
612
  } else if constexpr (32 == bitsof<U>()) {
613
    return ADDCARRYSUBBORROW32<Add>(c_in, static_cast<uint32_t>(a), static_cast<uint32_t>(b), reinterpret_cast<uint32_t>(out));
614
  } else if constexpr (64 == bitsof<U>()) {
615
    return ADDCARRYSUBBORROW64<Add>(c_in, static_cast<uint64_t>(a), static_cast<uint64_t>(b), reinterpret_cast<uint64_t>(out));
616
  } else if constexpr (0 == (bitsof<U>() % 64)) {
617
    using t64 = std::conditional<std::is_signed_v<U>, int64_t, uint64_t>;
618
    unsigned char carry;
619
    for (int i = 0; i < (bitsof<U>() / 64); i++) {
620
      carry = ADDCARRYSUBBORROW64<Add>(c_in, static_cast<t64>(a >> (i * 64)), static_cast<t64>(b >> (i * 64)), reinterpret_cast<t64>(out) + i);
621
    }
622
    return carry;
623
  } else {
624
    assert(((void)"add carry intrinsics support only support powers of 2 bits", false));
625
  }
626
}
5✔
627
#endif  // NO_X86_INTRINSICS
628

629
template <std::integral U>
630
static inline unsigned char add_carry(unsigned char c_in, U a, U b, U* out) noexcept {
10✔
631
  return add_carry_sub_borrow<true, U>(c_in, a, b, out);
10✔
632
}
5✔
633

634
template <std::integral U>
635
static inline unsigned char sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept {
636
  return add_carry_sub_borrow<false, U>(c_in, a, b, out);
637
}
638

639
// -------------------------------------------------------------------------- //
640

641
template <typename T, typename T128>
642
constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder) noexcept {
12✔
643
  constexpr auto digits = bitsof<T>();
12✔
644
  if constexpr (digits > bitsof<uint32_t>()) {
645
    T128 tmp128 = (static_cast<T128>(numerator_hi) << digits) | static_cast<T128>(numerator_lo);
646
    T quotient = static_cast<T>(tmp128 / static_cast<T128>(denominator));
647
    if (remainder) {
648
      *remainder = static_cast<T>(tmp128 % static_cast<T128>(denominator));
649
    }
650
    return quotient;
651
  } else {
6✔
652
    return _divx(numerator_hi, numerator_lo, denominator, remainder, std::ignore);
12✔
653
  }
6✔
654
}
6✔
655

656
template <typename T, typename... X>
657
constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder, X...) noexcept {
12✔
658
  constexpr auto digits = bitsof<T>();
12✔
659
  using wider_t = ceil_integral<digits + digits>;
6✔
660
  if constexpr ((digits + digits) <= binary_digits<wider_t>::value) {
6✔
661
    wider_t tmp = (static_cast<wider_t>(numerator_hi) << digits) | static_cast<wider_t>(numerator_lo);
12✔
662
    T quotient = static_cast<T>(tmp / static_cast<wider_t>(denominator));
12✔
663
    if (remainder) {
12!
664
      *remainder = static_cast<T>(tmp % static_cast<wider_t>(denominator));
12✔
665
    }
6✔
666
    return quotient;
12✔
667
  } else if constexpr (digits > bitsof<uint32_t>()) {
668
    const T& numhi = numerator_hi;
669
    const T& numlo = numerator_lo;
670
    const T& den = denominator;
671
    // We work in base 2**32.
672
    // A uint32 holds a single digit. A uint64 holds two digits.
673
    // Our numerator is conceptually [num3, num2, num1, num0].
674
    // Our denominator is [den1, den0].
675
    const uint64_t b = (1ull << 32);
676

677
    // The high and low digits of our computed quotient.
678
    uint32_t q1;
679
    uint32_t q0;
680

681
    // The normalization shift factor.
682
    int shift;
683

684
    // The high and low digits of our denominator (after normalizing).
685
    // Also the low 2 digits of our numerator (after normalizing).
686
    uint32_t den1;
687
    uint32_t den0;
688
    uint32_t num1;
689
    uint32_t num0;
690

691
    // A partial remainder.
692
    uint64_t rem;
693

694
    // The estimated quotient, and its corresponding remainder (unrelated to true remainder).
695
    uint64_t qhat;
696
    uint64_t rhat;
697

698
    // Variables used to correct the estimated quotient.
699
    uint64_t c1;
700
    uint64_t c2;
701

702
    // Check for overflow and divide by 0.
703
    if (numhi >= den) {
704
      if (remainder != nullptr) {
705
        *remainder = ~0ull;
706
      }
707
      return ~0ull;
708
    }
709

710
    // Determine the normalization factor. We multiply den by this, so that its leading digit is at
711
    // least half b. In binary this means just shifting left by the number of leading zeros, so that
712
    // there's a 1 in the MSB.
713
    // We also shift numer by the same amount. This cannot overflow because numhi < den.
714
    // The expression (-shift & 63) is the same as (64 - shift), except it avoids the UB of shifting
715
    // by 64. The funny bitwise 'and' ensures that numlo does not get shifted into numhi if shift is 0.
716
    // clang 11 has an x86 codegen bug here: see LLVM bug 50118. The sequence below avoids it.
717
    shift = __builtin_clzll(den);
718
    den <<= shift;
719
    numhi <<= shift;
720
    numhi |= (numlo >> (-shift & 63)) & (-(int64_t)shift >> 63);
721
    numlo <<= shift;
722

723
    // Extract the low digits of the numerator and both digits of the denominator.
724
    num1 = (uint32_t)(numlo >> 32);
725
    num0 = (uint32_t)(numlo & 0xFFFFFFFFu);
726
    den1 = (uint32_t)(den >> 32);
727
    den0 = (uint32_t)(den & 0xFFFFFFFFu);
728

729
    // We wish to compute q1 = [n3 n2 n1] / [d1 d0].
730
    // Estimate q1 as [n3 n2] / [d1], and then correct it.
731
    // Note while qhat may be 2 digits, q1 is always 1 digit.
732
    qhat = numhi / den1;
733
    rhat = numhi % den1;
734
    c1 = qhat * den0;
735
    c2 = rhat * b + num1;
736
    if (c1 > c2) {
737
      qhat -= (c1 - c2 > den) ? 2 : 1;
738
    }
739
    q1 = (uint32_t)qhat;
740

741
    // Compute the true (partial) remainder.
742
    rem = numhi * b + num1 - q1 * den;
743

744
    // We wish to compute q0 = [rem1 rem0 n0] / [d1 d0].
745
    // Estimate q0 as [rem1 rem0] / [d1] and correct it.
746
    qhat = rem / den1;
747
    rhat = rem % den1;
748
    c1 = qhat * den0;
749
    c2 = rhat * b + num0;
750
    if (c1 > c2) {
751
      qhat -= (c1 - c2 > den) ? 2 : 1;
752
    }
753
    q0 = (uint32_t)qhat;
754

755
    // Return remainder if requested.
756
    if (remainder != nullptr) {
757
      *remainder = (rem * b + num0 - q0 * den) >> shift;
758
    }
759
    return ((uint64_t)q1 << 32) | q0;
760
  } else {
761
    assert(false);
762
  }
763
}
6✔
764

765
// -------- IMPLEMENTATION DETAILS: INSTRUCTIONS: MULTIWORD MULTIPLY -------- //
766
// Multiplies src0 and src1 and gets the full result with compiler intrinsics
767
template <typename T, typename T128>
768
constexpr T _mulx(T src0, T src1, T* hi) noexcept {
12✔
769
  using wider_t = ceil_integral<bitsof<T>() + bitsof<T>()>;
6✔
770
  constexpr auto digits = bitsof<T>();
12✔
771
  if constexpr (digits > bitsof<uint32_t>()) {
772
    T128 tmp128 = static_cast<T128>(src0) * static_cast<T128>(src1);
773
    *hi = static_cast<T>(tmp128 >> digits);
774
    return static_cast<T>(tmp128);
775
  } else {
6✔
776
    return _mulx(src0, src1, hi, std::ignore);
12✔
777
  }
6✔
778
}
6✔
779

780
template <typename T, typename... X>
781
constexpr T _mulx(T src0, T src1, T* hi, X...) noexcept {
12✔
782
  constexpr T digits = bitsof<T>();
12✔
783
  using wider_t = ceil_integral<bitsof<T>() + bitsof<T>()>;
6✔
784
  if constexpr ((digits + digits) <= bitsof<wider_t>()) {
6✔
785
    wider_t tmp = static_cast<wider_t>(src0) * static_cast<wider_t>(src1);
12✔
786
    *hi = tmp >> digits;
12✔
787
    return static_cast<T>(tmp);
12✔
788
  } else {
789
    // Multiplies src0 and src1 and gets the full result without compiler intrinsics
790
    constexpr T offset = digits / 2;
791
    const T lsbs0 = src0 & _mask<T>(digits - offset);
792
    const T msbs0 = lsr(src0, offset);
793
    const T lsbs1 = src1 & _mask<T>(digits - offset);
794
    const T msbs1 = lsr(src1, offset);
795
    const T llsbs = lsbs0 * lsbs1;
796
    const T mlsbs = msbs0 * lsbs1;
797
    const T lmsbs = lsbs0 * msbs1;
798
    const T mi = mlsbs + lmsbs;
799
    const T lo = llsbs + static_cast<T>(mi << offset);
800
    const T lcarry = lo < llsbs || lo < static_cast<T>(mi << offset);
801
    const T mcarry = static_cast<T>(mi < mlsbs || mi < lmsbs) << offset;
802
    *hi = static_cast<T>(lsr(mi, offset)) + msbs0 * msbs1 + mcarry + lcarry;
803
    return lo;
804
  }
805
}
6✔
806
// -------------------------------------------------------------------------- //
807

808
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
809
constexpr auto with_bit_iterator_adapter(
21,455✔
810
    const bit_iterator<SrcIt>& first,
811
    const bit_iterator<SrcIt>& last,
812
    const bit_iterator<DstIt>& d_first) {
21,455✔
813
  using dst_word_type = typename bit_iterator<DstIt>::word_type;
21,455✔
814
  using src_word_type = typename bit_iterator<SrcIt>::word_type;
21,455✔
815
  if constexpr (!std::is_same_v<src_word_type, dst_word_type> && bitsof<src_word_type>() != bitsof<dst_word_type>()) {
21,455✔
816
    if constexpr (bitsof<src_word_type>() > bitsof<dst_word_type>()) {
9,054✔
817
      bit_iterator<bit_word_pointer_adapter<DstIt, SrcIt>> adapted_first(
18,108✔
818
          bit_word_pointer_adapter<DstIt, SrcIt>(first.base(), first.position() / bitsof<dst_word_type>()), first.position() % bitsof<dst_word_type>());
13,581✔
819
      bit_iterator<bit_word_pointer_adapter<DstIt, SrcIt>> adapted_last(
18,108✔
820
          bit_word_pointer_adapter<DstIt, SrcIt>(last.base(), last.position() / bitsof<dst_word_type>()), last.position() % bitsof<dst_word_type>());
13,581✔
821
      return AlgoFunc{}(adapted_first, adapted_last, d_first);
13,581✔
822
    } else {
4,527✔
823
      bit_iterator<bit_word_pointer_adapter<SrcIt, DstIt>> adapted_d_first(
18,108✔
824
          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✔
825
      return AlgoFunc{}(first, last, adapted_d_first);
13,581✔
826
    }
4,527✔
827
  } else {
12,401✔
828
    return AlgoFunc{}(first, last, d_first);
37,203✔
829
  }
12,401✔
830
}
21,455✔
831

832
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
833
constexpr auto with_bit_iterator_adapter(
25✔
834
    const SrcIt& first,
835
    const SrcIt& last,
836
    const bit_iterator<DstIt>& d_first) {
25✔
837
  return with_bit_iterator_adapter<AlgoFunc>(bit_iterator(first), bit_iterator(last), d_first);
75✔
838
}
25✔
839

840
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
841
constexpr auto with_bit_iterator_adapter(
8✔
842
    const bit_iterator<SrcIt>& first,
843
    const bit_iterator<SrcIt>& last,
844
    const DstIt& d_first) {
8✔
845
  return with_bit_iterator_adapter<AlgoFunc>(first, last, bit_iterator(d_first));
24✔
846
}
8✔
847

848
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
849
constexpr auto with_bit_iterator_adapter(
850
    const bit_iterator<SrcIt>& first,
851
    const bit_iterator<DstIt>& last) {
852
  using dst_word_type = typename bit_iterator<DstIt>::word_type;
853
  using src_word_type = typename bit_iterator<SrcIt>::word_type;
854
  if constexpr (!std::is_same_v<src_word_type, dst_word_type> && bitsof<src_word_type>() != bitsof<dst_word_type>()) {
855
    if constexpr (bitsof<src_word_type>() > bitsof<dst_word_type>()) {
856
      bit_iterator<bit_word_pointer_adapter<DstIt, SrcIt>> adapted_first(
857
          bit_word_pointer_adapter<DstIt, SrcIt>(first.base(), first.position() / bitsof<dst_word_type>()), first.position() % bitsof<dst_word_type>());
858
      return AlgoFunc{}(adapted_first, last);
859
    } else {
860
      bit_iterator<bit_word_pointer_adapter<SrcIt, DstIt>> adapted_last(
861
          bit_word_pointer_adapter<SrcIt, DstIt>(last.base(), last.position() / bitsof<src_word_type>()), last.position() % bitsof<src_word_type>());
862
      return AlgoFunc{}(first, adapted_last);
863
    }
864
  } else {
865
    return AlgoFunc{}(first, last);
866
  }
867
}
868

869
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
870
constexpr auto with_bit_iterator_adapter(
871
    const SrcIt& first,
872
    const bit_iterator<DstIt>& last) {
873
  return with_bit_iterator_adapter<AlgoFunc>(bit_iterator(first), last);
874
}
875

876
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
877
constexpr auto with_bit_iterator_adapter(
878
    const bit_iterator<SrcIt>& first,
879
    const DstIt& last) {
880
  return with_bit_iterator_adapter<AlgoFunc>(first, bit_iterator(last));
881
}
882

883
namespace detail {
884

885
struct uninitialized_t {
886
  explicit uninitialized_t() = default;
887
};
888
inline constexpr uninitialized_t uninitialized{};
889

890
template <typename size_type, bool resizeable, std::size_t Extent>
891
struct container_size_storage {
892
  constexpr size_type size() const noexcept {
300✔
893
    return Extent;
300✔
894
  }
150✔
895

896
  constexpr container_size_storage() noexcept {}
295✔
897
};
898

899
template <typename size_type, bool resizeable>
900
struct container_size_storage<size_type, resizeable, std::dynamic_extent> {
901
  using maybe_const_size_type = std::conditional_t<resizeable, size_type, std::add_const_t<size_type>>;
902

903
  maybe_const_size_type size_;
904
  constexpr size_type size() const noexcept {
3,930✔
905
    return size_;
3,930✔
906
  }
1,965✔
907
  constexpr void resize(const size_type& new_size)
908
    requires(resizeable)
909
  {
910
    size_ = new_size;
911
  }
912

913
  constexpr container_size_storage() noexcept : size_() {}
914
  constexpr container_size_storage(const size_type& size) noexcept
123✔
915
      : size_(size) {}
247✔
916
};
917

918
}  // namespace detail
919

920
// ========================================================================== //
921
}  // namespace bit
922
#endif // _BIT_DETAILS_HPP_INCLUDED
923
       // ========================================================================== //
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