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

PeterCDMcLean / BitLib / 16834746609

08 Aug 2025 03:53PM UTC coverage: 76.567% (-0.2%) from 76.797%
16834746609

Pull #29

github

web-flow
Merge 50241d764 into 4adca69e9
Pull Request #29: Fix string conversion and many warnings

3341 of 5028 branches covered (66.45%)

Branch coverage included in aggregate %.

222 of 230 new or added lines in 17 files covered. (96.52%)

12 existing lines in 3 files now uncovered.

2573 of 2696 relevant lines covered (95.44%)

29259346.65 hits per line

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

70.68
/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, size_t start, size_t len) noexcept;
209
template <class T, class... X>
210
constexpr T _bextr(T src, size_t start, size_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(const T& dst, const T& src, const size_t& cnt) noexcept;
231

232
// Double precision shift right
233
template <class T>
234
constexpr T _shrd(const T& dst, const T& src, const size_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) {
175,863,401✔
252
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
87,866,280✔
253
  assert(static_cast<size_t>(shift) < bitsof<T>());
175,863,401!
254
#endif
87,866,280✔
255
  return static_cast<T>(static_cast<std::make_unsigned_t<T>>(val) >> shift);
175,863,401✔
256
}
87,866,280✔
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(static_cast<size_t>(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(static_cast<size_t>(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(static_cast<size_t>(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
enum class _mask_start {
300
  unknown,
301
  in_range
302
};
303

304
template <std::integral T, _mask_len len_in_range = _mask_len::in_range, typename size_type = size_t>
305
constexpr T _mask(const size_type len) {
49,136,918✔
306
  using unsigned_t = std::make_unsigned_t<T>;
23,731,941✔
307
  constexpr unsigned_t one = unsigned_t(1);
49,136,918✔
308
  if constexpr (len_in_range != _mask_len::unknown) {
23,731,941✔
309
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
927,204✔
310
    assert(static_cast<size_t>(len) < bitsof<T>());
1,854,809!
311
#endif
927,204✔
312
    return static_cast<T>((one << static_cast<unsigned_t>(len)) - one);
1,854,809✔
313
  } else {
22,804,737✔
314
    // The digits_mask is solely here to prevent Undefined Sanitizer
315
    // complaining about shift of len >= digits
316
    // Note: on -O1 the (len & digits_mask) is optimized to simply (len)
317
    constexpr unsigned_t digits_mask = static_cast<unsigned_t>(bitsof<T>()) - one;
47,282,109✔
318
    return static_cast<T>((one << (static_cast<unsigned_t>(len) & digits_mask)) * static_cast<unsigned_t>(len < bitsof<T>()) - one);
47,282,109✔
319
  }
22,804,737✔
320
}
23,731,941✔
321
template <std::integral T,
322
          _mask_len len_in_range = _mask_len::in_range,
323
          _mask_start start_in_range = _mask_start::in_range,
324
          typename size_type = size_t>
325
constexpr T _mask(const size_type len, const size_type start) {
6,383,594✔
326
  if constexpr (start_in_range != _mask_start::unknown) {
2,211,529✔
327
    return static_cast<T>(_mask<T, len_in_range>(len) << start);
18,316✔
328
  } else {
2,202,534✔
329
    return static_cast<T>((_mask<T, len_in_range>(len) << start) * (start < bitsof<T>()));
6,365,278✔
330
  }
2,202,534✔
331
}
2,211,529✔
332

333
// ------------- IMPLEMENTATION DETAILS: UTILITIES: ASSERTIONS -------------- //
334
// If the range allows multipass iteration, checks if last - first >= 0
335
template <class Iterator>
336
constexpr bool _assert_range_viability(Iterator first, Iterator last) {
5,701,202✔
337
  using traits_t = std::iterator_traits<Iterator>;
2,846,668✔
338
  using category_t = typename traits_t::iterator_category;
2,846,668✔
339
  using multi_t = std::forward_iterator_tag;
2,846,668✔
340
  constexpr bool is_multipass = std::is_base_of<multi_t, category_t>::value;
5,701,202✔
341
  const bool is_viable = !is_multipass || std::distance(first, last) >= 0;
8,555,736!
342
  assert(is_viable);
5,701,202!
343
  return is_viable;
8,555,736✔
344
}
2,846,668✔
345
// -------------------------------------------------------------------------- //
346

347
// ------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT FIELD EXTRACTION ------- //
348
// Extacts to lsbs a field of contiguous bits with compiler intrinsics
349
template <class T, class>
350
constexpr T _bextr(T src, size_t start, size_t len) noexcept {
351
  static_assert(binary_digits<T>::value, "");
352
  constexpr size_t digits = binary_digits<T>::value;
353
  T dst = T();
354
  if (digits <= std::numeric_limits<unsigned int>::digits) {
355
    dst = __builtin_ia32_bextr_u32(src, start, len);
356
  } else if (digits <= std::numeric_limits<unsigned long long int>::digits) {
357
    dst = __builtin_ia32_bextr_u64(src, start, len);
358
  } else {
359
    dst = _bextr(src, start, len, std::ignore);
360
  }
361
  return dst;
362
}
363

364
// Extacts to lsbs a field of contiguous bits without compiler intrinsics
365
template <class T, class... X>
366
constexpr T _bextr(T src, size_t start, size_t len, X...) noexcept {
15,104✔
367
  static_assert(binary_digits<T>::value, "");
7,616✔
368
  const T msk = _mask<T, _mask_len::unknown>(len);
15,104✔
369
  return (lsr(src, start))&msk;
15,104✔
370
}
7,616✔
371
// -------------------------------------------------------------------------- //
372

373
// ------------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT SWAP ------------- //
374
// Reverses the order of the bits with or without of compiler intrinsics
375
template <class T>
376
constexpr T _bitswap(T src) noexcept {
21,042,880✔
377
  static_assert(binary_digits<T>::value, "");
10,521,728✔
378
  using byte_t = unsigned char;
10,521,728✔
379
  constexpr T digits = binary_digits<T>::value;
21,042,880✔
380
  constexpr unsigned long long int first = 0x80200802ULL;
21,042,880✔
381
  constexpr unsigned long long int second = 0x0884422110ULL;
21,042,880✔
382
  constexpr unsigned long long int third = 0x0101010101ULL;
21,042,880✔
383
  constexpr unsigned long long int fourth = 32;
21,042,880✔
384
  constexpr bool is_size1 = sizeof(T) == 1;
21,042,880✔
385
  constexpr bool is_byte = digits == std::numeric_limits<byte_t>::digits;
21,042,880✔
386
  constexpr bool is_octet = std::numeric_limits<byte_t>::digits == 8;
21,042,880✔
387
  constexpr bool is_pow2 = std::has_single_bit(static_cast<std::make_unsigned_t<T>>(digits));
21,042,880✔
388
  T dst = src;
21,042,880✔
389
  T i = digits - 1;
21,042,880✔
390
  if (is_size1 && is_byte && is_octet) {
10,521,728✔
391
    dst = static_cast<T>(lsr(((static_cast<std::make_unsigned_t<T>>(src) * first) & second) * third, fourth));
5,245,200✔
392
  } else if (is_pow2) {
7,899,296✔
393
    dst = _bitswap<T, digits>(src);
15,797,680✔
394
  } else {
7,899,296✔
395
    for (src = lsr(src, 1); src; src = lsr(src, 1)) {
×
396
      dst <<= 1;
397
      dst |= src & 1;
398
      i--;
399
    }
400
    dst <<= i;
401
  }
402
  return dst;
21,042,880✔
403
}
10,521,728✔
404

405
// Reverses the order of the bits: recursive metafunction
406
template <class T, std::size_t N>
407
constexpr T _bitswap(T src) noexcept {
79,020,032✔
408
  static_assert(binary_digits<T>::value, "");
39,511,840✔
409
  constexpr T cnt = N >> 1;
79,020,032✔
410
  constexpr T msk = _bitswap<T, cnt>();
79,020,032✔
411
  src = static_cast<T>(((lsr(src, cnt))&msk) | ((src << cnt) & ~msk));
79,020,032✔
412
  return cnt > 1 ? _bitswap<T, cnt>(src) : src;
79,020,032✔
413
}
39,511,840✔
414

415
// Reverses the order of the bits: mask for the recursive metafunction
416
template <class T, std::size_t N>
417
constexpr T _bitswap() noexcept {
418
  static_assert(binary_digits<T>::value, "");
419
  constexpr T digits = binary_digits<T>::value;
420
  T cnt = digits;
421
  T msk = ~T();
422
  while (cnt != N) {
423
    cnt = lsr(cnt, 1);
NEW
424
    msk ^= static_cast<T>(msk << cnt);
425
  }
426
  return msk;
427
}
428
// -------------------------------------------------------------------------- //
429

430
// ------------ IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT BLEND ------------- //
431
// Replaces bits of src0 by the ones of src1 where the mask is true
432

433
template <typename T, typename U>
434
constexpr exact_floor_integral_t<T> _bitblend(
651,577✔
435
    const T src0_,
436
    const U src1_,
437
    const exact_floor_integral_t<T> msk) noexcept
438
  requires(std::is_same_v<exact_floor_integral_t<T>, exact_floor_integral_t<U>>)
439
{
651,383✔
440
  static_assert(binary_digits<exact_floor_integral_t<T>>::value, "");
651,383✔
441
  const exact_floor_integral_t<T> src0 = static_cast<exact_floor_integral_t<T>>(src0_);
1,302,960✔
442
  const exact_floor_integral_t<U> src1 = static_cast<exact_floor_integral_t<U>>(src1_);
1,302,960✔
443
  return src0 ^ ((src0 ^ src1) & msk);
1,302,960✔
444
}
651,383✔
445

446
// Replaces len bits of src0 by the ones of src1 starting at start
447
template <typename T, typename U>
448
constexpr exact_floor_integral_t<T> _bitblend(
4,157,620✔
449
    const T src0_,
450
    const U src1_,
451
    const size_t start,
452
    const size_t len) noexcept
453
  requires(std::is_same_v<exact_floor_integral_t<T>, exact_floor_integral_t<U>>)
454
{
2,200,164✔
455
  using resolved_t = exact_floor_integral_t<T>;
2,200,164✔
456
  using promoted_t = std::conditional_t<bitsof<resolved_t>() < bitsof<int>(), int, resolved_t>;
2,200,164✔
457
  static_assert(binary_digits<exact_floor_integral_t<T>>::value, "");
2,200,164✔
458
  const promoted_t src0 = static_cast<promoted_t>(src0_);
6,357,784✔
459
  const promoted_t src1 = static_cast<promoted_t>(src1_);
6,357,784✔
460
  const resolved_t msk = _mask<resolved_t, _mask_len::unknown, _mask_start::unknown>(len, start);
6,357,784✔
461
  return static_cast<resolved_t>(src0 ^ ((src0 ^ src1) & msk));
6,357,784✔
462
}
2,200,164✔
463
// -------------------------------------------------------------------------- //
464

465
// ---------- IMPLEMENTATION DETAILS: INSTRUCTIONS: BIT EXCHANGE ------------ //
466
// Exchanges/swaps bits of src0 by the ones of src1 where the mask is true
467
template <class T>
468
constexpr void _bitexch(T& src0, T& src1, T msk) noexcept {
469
  src0 = src0 ^ static_cast<T>(src1 & msk);
470
  src1 = src1 ^ static_cast<T>(src0 & msk);
471
  src0 = src0 ^ static_cast<T>(src1 & msk);
472
  return;
473
}
474

475
// Replaces len bits of src0 by the ones of src1 starting at start
476
template <class T, class S>
477
constexpr void _bitexch(T& src0, T& src1, S start, S len) noexcept {
7,494✔
478
  static_assert(binary_digits<T>::value, "");
2,370✔
479
  const T msk = _mask<T, _mask_len::unknown, _mask_start::unknown>(len, start);
7,494✔
480
  src0 = src0 ^ static_cast<T>(src1 & msk);
7,494✔
481
  src1 = src1 ^ static_cast<T>(src0 & msk);
7,494✔
482
  src0 = src0 ^ static_cast<T>(src1 & msk);
7,494✔
483
}
7,494✔
484

485
// Replaces len bits of src0 by the ones of src1 starting at start0
486
// in src0 and start1 in src1.
487
// len <= digits-max(start0, start1)
488
// clang-format off
489
template <class T, class S>
490
constexpr void _bitexch(T& src0, T& src1, S start0, S start1, S len) noexcept
20,307,140✔
491
{
20,594,587✔
492
    static_assert(binary_digits<T>::value, "");
20,594,587✔
493
    const T msk = _mask<T, _mask_len::unknown>(len);
40,901,727✔
494
    if (start0 >= start1) {
40,901,727✔
495
        src0 = src0 ^ (
30,612,099✔
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
        src1 = src1 ^ (
20,455,439✔
501
                static_cast<T>(lsr(src0, (start0 - start1)))
20,455,439✔
502
                &
20,455,439✔
503
                static_cast<T>(msk << start1)
30,612,099✔
504
        );
10,298,779✔
505
        src0 = src0 ^ (
40,768,759✔
506
                static_cast<T>(src1 << (start0 - start1))
20,455,439✔
507
                &
20,455,439✔
508
                static_cast<T>(msk << start0)
30,612,099✔
509
        );
10,298,779✔
510
    } else {
10,298,779✔
511
        src0 = src0 ^ (
20,446,288✔
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
        src1 = src1 ^ (
30,596,768✔
517
                static_cast<T>(src0 << (start1 - start0))
20,446,288✔
518
                &
20,446,288✔
519
                static_cast<T>(msk << start1)
30,596,768✔
520
        );
10,295,808✔
521
        src0 = src0 ^ (
30,596,768✔
522
                static_cast<T>(lsr(src1, (start1 - start0)))
20,446,288✔
523
                &
20,446,288✔
524
                static_cast<T>(msk << start0)
30,596,768✔
525
        );
10,295,808✔
526
    }
10,295,808✔
527
    return;
40,901,727✔
528
}
20,594,587✔
529
// clang-format on
530
// -------------------------------------------------------------------------- //
531

532
// --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT LEFT ---- //
533
// Left shifts dst by cnt bits, filling the lsbs of dst by the msbs of src
534
template <class T>
535
constexpr T _shld(const T& dst, const T& src, const size_t& cnt) noexcept {
536
  static_assert(binary_digits<T>::value, "");
537
  constexpr size_t digits = binary_digits<T>::value;
538
  if (cnt < digits) {
539
    return static_cast<T>(lsl(dst, cnt) | (lsr(src, (digits - cnt))));
540
  } else {
541
    return static_cast<T>(lsl(src, cnt - digits) * (cnt < (digits + digits)));
542
  }
543
}
544
// -------------------------------------------------------------------------- //
545

546
// --- IMPLEMENTATION DETAILS: INSTRUCTIONS: DOUBLE PRECISION SHIFT RIGHT --- //
547
// Right shifts dst by cnt bits, filling the msbs of dst by the lsbs of src
548
template <class T>
549
constexpr T _shrd(const T& dst, const T& src, const size_t& cnt) noexcept {
22,279,610✔
550
  static_assert(binary_digits<T>::value, "");
11,838,264✔
551
  constexpr size_t digits = binary_digits<T>::value;
22,279,610✔
552
  if (cnt < digits) {
22,279,610!
553
    return static_cast<T>((lsr(dst, cnt)) | lsl(src, (digits - cnt)));
22,279,610✔
554
  } else {
11,838,264✔
NEW
555
    return static_cast<T>((lsr(src, (cnt - digits))) * (cnt < (digits + digits)));
×
556
  }
557
}
11,838,264✔
558
// -------------------------------------------------------------------------- //
559

560
#if defined(NO_X86_INTRINSICS)
561
template <bool Add, std::integral U>
562
static inline unsigned char add_carry_sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept {
563
  static_assert(std::is_unsigned_v<U>, "Only unsigned types are supported");
564

565
  if constexpr (Add) {
566
    U sum1 = a + b;
567
    U sum2 = sum1 + static_cast<U>(c_in);
568
    *out = sum2;
569

570
    // Carry occurs if either sum1 overflows a+b or sum2 overflows sum1+carry
571
    return static_cast<unsigned char>((sum1 < a) || (sum2 < sum1));
572
  } else {
573
    U diff1 = a - b;
574
    U diff2 = diff1 - static_cast<U>(c_in);
575
    *out = diff2;
576

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

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

637
template <std::integral U>
638
static inline unsigned char add_carry(unsigned char c_in, U a, U b, U* out) noexcept {
10✔
639
  return add_carry_sub_borrow<true, U>(c_in, a, b, out);
10✔
640
}
5✔
641

642
template <std::integral U>
643
static inline unsigned char sub_borrow(unsigned char c_in, U a, U b, U* out) noexcept {
644
  return add_carry_sub_borrow<false, U>(c_in, a, b, out);
645
}
646

647
// -------------------------------------------------------------------------- //
648

649
template <typename T, typename T128>
650
constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder) noexcept {
18✔
651
  constexpr auto digits = bitsof<T>();
18✔
652
  if constexpr (digits > bitsof<uint32_t>()) {
9✔
653
    T128 tmp128 = (static_cast<T128>(numerator_hi) << digits) | static_cast<T128>(numerator_lo);
×
654
    T quotient = static_cast<T>(tmp128 / static_cast<T128>(denominator));
×
655
    if (remainder) {
×
656
      *remainder = static_cast<T>(tmp128 % static_cast<T128>(denominator));
×
657
    }
658
    return quotient;
×
659
  } else {
9✔
660
    return _divx(numerator_hi, numerator_lo, denominator, remainder, std::ignore);
18✔
661
  }
9✔
662
}
9✔
663

664
// This code is from a 'public domain' post of ridiculousfish:
665
// https://ridiculousfish.com/blog/posts/labor-of-division-episode-v.html
666
template <typename T, typename... X>
667
constexpr T _divx(const T& numerator_hi, const T& numerator_lo, const T& denominator, T* remainder, X...) noexcept {
18✔
668
  constexpr auto digits = bitsof<T>();
18✔
669
  using wider_t = ceil_integral<digits + digits>;
9✔
670
  if constexpr ((digits + digits) <= binary_digits<wider_t>::value) {
9✔
671
    wider_t tmp = (static_cast<wider_t>(numerator_hi) << digits) | static_cast<wider_t>(numerator_lo);
18✔
672
    T quotient = static_cast<T>(tmp / static_cast<wider_t>(denominator));
18✔
673
    if (remainder) {
18!
674
      *remainder = static_cast<T>(tmp % static_cast<wider_t>(denominator));
18✔
675
    }
9✔
676
    return quotient;
18✔
677
  } else if constexpr (digits > bitsof<uint32_t>()) {
678
    T numhi = numerator_hi;
679
    T numlo = numerator_lo;
680
    T den = denominator;
681
    // We work in base 2**32.
682
    // A uint32 holds a single digit. A uint64 holds two digits.
683
    // Our numerator is conceptually [num3, num2, num1, num0].
684
    // Our denominator is [den1, den0].
685
    const uint64_t b = (1ull << 32);
686

687
    // The high and low digits of our computed quotient.
688
    uint32_t q1;
689
    uint32_t q0;
690

691
    // The normalization shift factor.
692
    int shift;
693

694
    // The high and low digits of our denominator (after normalizing).
695
    // Also the low 2 digits of our numerator (after normalizing).
696
    uint32_t den1;
697
    uint32_t den0;
698
    uint32_t num1;
699
    uint32_t num0;
700

701
    // A partial remainder.
702
    uint64_t rem;
703

704
    // The estimated quotient, and its corresponding remainder (unrelated to true remainder).
705
    uint64_t qhat;
706
    uint64_t rhat;
707

708
    // Variables used to correct the estimated quotient.
709
    uint64_t c1;
710
    uint64_t c2;
711

712
    // Check for overflow and divide by 0.
713
    if (numhi >= den) {
714
      if (remainder != nullptr) {
715
        *remainder = ~0ull;
716
      }
717
      return ~0ull;
718
    }
719

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

733
    // Extract the low digits of the numerator and both digits of the denominator.
734
    num1 = static_cast<uint32_t>(numlo >> 32);
735
    num0 = static_cast<uint32_t>(numlo & 0xFFFFFFFFu);
736
    den1 = static_cast<uint32_t>(den >> 32);
737
    den0 = static_cast<uint32_t>(den & 0xFFFFFFFFu);
738

739
    // We wish to compute q1 = [n3 n2 n1] / [d1 d0].
740
    // Estimate q1 as [n3 n2] / [d1], and then correct it.
741
    // Note while qhat may be 2 digits, q1 is always 1 digit.
742
    qhat = numhi / den1;
743
    rhat = numhi % den1;
744
    c1 = qhat * den0;
745
    c2 = rhat * b + num1;
746
    if (c1 > c2) {
747
      qhat -= (c1 - c2 > den) ? 2 : 1;
748
    }
749
    q1 = static_cast<uint32_t>(qhat);
750

751
    // Compute the true (partial) remainder.
752
    rem = numhi * b + num1 - q1 * den;
753

754
    // We wish to compute q0 = [rem1 rem0 n0] / [d1 d0].
755
    // Estimate q0 as [rem1 rem0] / [d1] and correct it.
756
    qhat = rem / den1;
757
    rhat = rem % den1;
758
    c1 = qhat * den0;
759
    c2 = rhat * b + num0;
760
    if (c1 > c2) {
761
      qhat -= (c1 - c2 > den) ? 2 : 1;
762
    }
763
    q0 = static_cast<uint32_t>(qhat);
764

765
    // Return remainder if requested.
766
    if (remainder != nullptr) {
767
      *remainder = (rem * b + num0 - q0 * den) >> shift;
768
    }
769
    return (static_cast<uint64_t>(q1) << 32) | q0;
770
  } else {
771
    assert(false);
772
  }
773
}
9✔
774

775
// -------- IMPLEMENTATION DETAILS: INSTRUCTIONS: MULTIWORD MULTIPLY -------- //
776
// Multiplies src0 and src1 and gets the full result with compiler intrinsics
777
template <typename T, typename T128>
778
constexpr T _mulx(T src0, T src1, T* hi) noexcept {
12✔
779
  constexpr auto digits = bitsof<T>();
12✔
780
  if constexpr (digits > bitsof<uint32_t>()) {
6✔
UNCOV
781
    T128 tmp128 = static_cast<T128>(src0) * static_cast<T128>(src1);
×
UNCOV
782
    *hi = static_cast<T>(tmp128 >> digits);
×
UNCOV
783
    return static_cast<T>(tmp128);
×
784
  } else {
6✔
785
    return _mulx(src0, src1, hi, std::ignore);
12✔
786
  }
6✔
787
}
6✔
788

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

817
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
818
constexpr auto with_bit_iterator_adapter(
1,058,198✔
819
    const bit_iterator<SrcIt>& first,
820
    const bit_iterator<SrcIt>& last,
821
    const bit_iterator<DstIt>& d_first) {
1,058,198✔
822
  using dst_word_type = typename bit_iterator<DstIt>::word_type;
1,058,198✔
823
  using src_word_type = typename bit_iterator<SrcIt>::word_type;
1,058,198✔
824
  if constexpr (!std::is_same_v<src_word_type, dst_word_type> && bitsof<src_word_type>() != bitsof<dst_word_type>()) {
1,058,198✔
825
    if constexpr (bitsof<src_word_type>() > bitsof<dst_word_type>()) {
9,964✔
826
      bit_iterator<bit_word_pointer_adapter<DstIt, SrcIt>> adapted_first(
21,748✔
827
          bit_word_pointer_adapter<DstIt, SrcIt>(first.base(), first.position() / bitsof<dst_word_type>()), first.position() % bitsof<dst_word_type>());
16,311✔
828
      bit_iterator<bit_word_pointer_adapter<DstIt, SrcIt>> adapted_last(
21,748✔
829
          bit_word_pointer_adapter<DstIt, SrcIt>(last.base(), last.position() / bitsof<dst_word_type>()), last.position() % bitsof<dst_word_type>());
16,311✔
830
      return AlgoFunc{}(adapted_first, adapted_last, d_first);
16,311✔
831
    } else {
5,437✔
832
      bit_iterator<bit_word_pointer_adapter<SrcIt, DstIt>> adapted_d_first(
18,108✔
833
          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✔
834
      return AlgoFunc{}(first, last, adapted_d_first);
13,581✔
835
    }
4,527✔
836
  } else {
1,048,234✔
837
    return AlgoFunc{}(first, last, d_first);
3,144,702✔
838
  }
1,048,234✔
839
}
1,058,198✔
840

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

849
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
850
constexpr auto with_bit_iterator_adapter(
8✔
851
    const bit_iterator<SrcIt>& first,
852
    const bit_iterator<SrcIt>& last,
853
    const DstIt& d_first) {
8✔
854
  return with_bit_iterator_adapter<AlgoFunc>(first, last, bit_iterator(d_first));
24✔
855
}
8✔
856

857
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
858
constexpr auto with_bit_iterator_adapter(
859
    const bit_iterator<SrcIt>& first,
860
    const bit_iterator<DstIt>& last) {
861
  using dst_word_type = typename bit_iterator<DstIt>::word_type;
862
  using src_word_type = typename bit_iterator<SrcIt>::word_type;
863
  if constexpr (!std::is_same_v<src_word_type, dst_word_type> && bitsof<src_word_type>() != bitsof<dst_word_type>()) {
864
    if constexpr (bitsof<src_word_type>() > bitsof<dst_word_type>()) {
865
      bit_iterator<bit_word_pointer_adapter<DstIt, SrcIt>> adapted_first(
866
          bit_word_pointer_adapter<DstIt, SrcIt>(first.base(), first.position() / bitsof<dst_word_type>()), first.position() % bitsof<dst_word_type>());
867
      return AlgoFunc{}(adapted_first, last);
868
    } else {
869
      bit_iterator<bit_word_pointer_adapter<SrcIt, DstIt>> adapted_last(
870
          bit_word_pointer_adapter<SrcIt, DstIt>(last.base(), last.position() / bitsof<src_word_type>()), last.position() % bitsof<src_word_type>());
871
      return AlgoFunc{}(first, adapted_last);
872
    }
873
  } else {
874
    return AlgoFunc{}(first, last);
875
  }
876
}
877

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

885
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
886
constexpr auto with_bit_iterator_adapter(
887
    const bit_iterator<SrcIt>& first,
888
    const DstIt& last) {
889
  return with_bit_iterator_adapter<AlgoFunc>(first, bit_iterator(last));
890
}
891

892
namespace detail {
893

894
struct uninitialized_t {
895
  explicit uninitialized_t() = default;
896
};
897
inline constexpr uninitialized_t uninitialized{};
898

899
struct initialized_t {
900
  explicit initialized_t() = default;
901
};
902
inline constexpr initialized_t initialized{};
903

904
template <typename size_type, bool resizeable, std::size_t Extent>
905
struct container_size_storage {
906
  constexpr size_type size() const noexcept {
7,582✔
907
    return Extent;
7,582✔
908
  }
3,791✔
909

910
  constexpr container_size_storage() noexcept {}
5,757✔
911
};
912

913
template <typename size_type, bool resizeable>
914
struct container_size_storage<size_type, resizeable, std::dynamic_extent> {
915
  using maybe_const_size_type = std::conditional_t<resizeable, size_type, std::add_const_t<size_type>>;
916

917
  maybe_const_size_type size_;
918
  constexpr size_type size() const noexcept {
8,404,882✔
919
    return size_;
8,404,882✔
920
  }
4,202,441✔
921
  constexpr void resize(const size_type& new_size)
922
    requires(resizeable)
923
  {
924
    size_ = new_size;
925
  }
926

927
  constexpr container_size_storage() noexcept : size_() {}
928
  constexpr container_size_storage(const size_type& size) noexcept
789,143✔
929
      : size_(size) {}
1,578,287✔
930
};
931

932
}  // namespace detail
933

934
// ========================================================================== //
935
}  // namespace bit
936
#endif // _BIT_DETAILS_HPP_INCLUDED
937
       // ========================================================================== //
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