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

PeterCDMcLean / BitLib / 16688867468

02 Aug 2025 02:24AM UTC coverage: 74.757% (-3.7%) from 78.485%
16688867468

push

github

web-flow
From string (#18)

* Fix size-copy construction for bitwise ops

* Primitive from_string

* Add iterator-argument flavors of typical policy

* Allow copy to take native iterators and auto-convert

* Use a single type / value to template string conversion

* Rebase cleanup

* Replace auto and fix namespace issues in to_from_string.hpp

* Use proper aggregate initialization for array

* clang being naughty

* Fix to string length calculation

* Some scalar Addition and Multiplication algorithms

* transform must mask operands to functor

* Don't try to convert before bitsof

* Fix carry out for sub words

* Division by scalar

* Add define for checking undefined behavior

* Basic from_string working

* Base 10 to string

* Non-templated to/from string functions

* to_string can be bounded by string iterators

* Reorder to_string arguments for in->out order

* Add streaming operator to array_base

* Fix error C2361 case variable initialization

* Add step to download and extract clang compiler rt libs for windows

* Replace instrinsic with std

* Give up on directory flag to tar

* Simplify cache step

* Add new line to license. seems reasonable

* Don't use the iterator below begin()

* Allow examples to be built as if they were the root. Add a dynamic bit-aggregate type example

* Optimize to string by change zero check to reduce computation size

3451 of 5352 branches covered (64.48%)

Branch coverage included in aggregate %.

346 of 470 new or added lines in 12 files covered. (73.62%)

28 existing lines in 2 files now uncovered.

2555 of 2682 relevant lines covered (95.26%)

29850565.99 hits per line

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

69.96
/include/bitlib/bit-iterator/bit_details.hpp
1
// ============================== BIT DETAILS =============================== //
2
// Project:         The C++ Bit Library
3
// Name:            bit_details.hpp
4
// Description:     Provides common implementation details and helper classes
5
// Creator:         Vincent Reverdy
6
// Contributor(s):  Vincent Reverdy [2015-2017]
7
//                  Bryce Kille [2019]
8
// License:         BSD 3-Clause License
9
// ========================================================================== //
10
#ifndef _BIT_DETAILS_HPP_INCLUDED
11
#define _BIT_DETAILS_HPP_INCLUDED
12
// ========================================================================== //
13

14

15

16
// ================================ PREAMBLE ================================ //
17
// C++ standard library
18
// 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,217✔
252
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
86,079,188✔
253
  assert(shift < bitsof<T>());
172,289,217!
254
#endif
86,079,188✔
255
  return static_cast<T>(static_cast<std::make_unsigned_t<T>>(val) >> shift);
172,289,217✔
256
}
86,079,188✔
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,791✔
302
  constexpr std::make_unsigned_t<T> one = std::make_unsigned_t<T>(1);
45,321,791✔
303
  if constexpr (len_in_range != _mask_len::unknown) {
21,824,269✔
304
#ifdef BITLIB_DETECT_UNDEFINED_SHIFT
29,018✔
305
    assert(len < bitsof<T>());
58,437!
306
#endif
29,018✔
307
    return static_cast<T>((one << len) - one);
58,437✔
308
  } else {
21,795,251✔
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,354✔
313
    return static_cast<T>((one << (len & digits_mask)) * (len < bitsof<T>()) - one);
45,263,354✔
314
  }
21,795,251✔
315
}
21,824,269✔
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,070✔
321
  using traits_t = std::iterator_traits<Iterator>;
509,602✔
322
  using category_t = typename traits_t::iterator_category;
509,602✔
323
  using multi_t = std::forward_iterator_tag;
509,602✔
324
  constexpr bool is_multipass = std::is_base_of<multi_t, category_t>::value;
1,027,070✔
325
  const bool is_viable = !is_multipass || std::distance(first, last) >= 0;
1,544,538!
326
  assert(is_viable);
1,027,070!
327
  return is_viable;
1,544,538✔
328
}
509,602✔
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,104✔
351
  static_assert(binary_digits<T>::value, "");
7,616✔
352
  constexpr T digits = binary_digits<T>::value;
15,104✔
353
  constexpr T one = 1;
15,104✔
354
  const T msk = (one << len) * (len < digits) - one;
15,104✔
355
  return (lsr(src, start)) & msk * (start < digits);
15,104✔
356
}
7,616✔
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,691✔
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,497✔
427
  static_assert(binary_digits<exact_floor_integral_t<T>>::value, "");
11,497✔
428
  const exact_floor_integral_t<T> src0 = static_cast<exact_floor_integral_t<T>>(src0_);
23,188✔
429
  const exact_floor_integral_t<U> src1 = static_cast<exact_floor_integral_t<U>>(src1_);
23,188✔
430
  return src0 ^ ((src0 ^ src1) & msk);
23,188✔
431
}
11,497✔
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,863✔
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,407✔
442
  static_assert(binary_digits<exact_floor_integral_t<T>>::value, "");
1,198,407✔
443
  constexpr exact_floor_integral_t<T> digits = bitsof<exact_floor_integral_t<T>>();
4,354,270✔
444
  const exact_floor_integral_t<T> src0 = static_cast<exact_floor_integral_t<T>>(src0_);
4,354,270✔
445
  const exact_floor_integral_t<U> src1 = static_cast<exact_floor_integral_t<U>>(src1_);
4,354,270✔
446
  const exact_floor_integral_t<T> msk = _mask<exact_floor_integral_t<T>, _mask_len::unknown>(len) << start;
4,354,270✔
447
  return src0 ^ ((src0 ^ src1) & msk * (start < digits));
4,354,270✔
448
}
1,198,407✔
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>()) {
5✔
NEW
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 {
18✔
643
  constexpr auto digits = bitsof<T>();
18✔
644
  if constexpr (digits > bitsof<uint32_t>()) {
9✔
NEW
645
    T128 tmp128 = (static_cast<T128>(numerator_hi) << digits) | static_cast<T128>(numerator_lo);
×
NEW
646
    T quotient = static_cast<T>(tmp128 / static_cast<T128>(denominator));
×
NEW
647
    if (remainder) {
×
NEW
648
      *remainder = static_cast<T>(tmp128 % static_cast<T128>(denominator));
×
NEW
649
    }
NEW
650
    return quotient;
×
651
  } else {
9✔
652
    return _divx(numerator_hi, numerator_lo, denominator, remainder, std::ignore);
18✔
653
  }
9✔
654
}
9✔
655

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

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

683
    // The normalization shift factor.
684
    int shift;
685

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

693
    // A partial remainder.
694
    uint64_t rem;
695

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

700
    // Variables used to correct the estimated quotient.
701
    uint64_t c1;
702
    uint64_t c2;
703

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

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

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

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

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

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

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

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

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

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

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

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

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

871
template <typename AlgoFunc, std::random_access_iterator SrcIt, std::random_access_iterator DstIt>
872
constexpr auto with_bit_iterator_adapter(
873
    const SrcIt& first,
874
    const bit_iterator<DstIt>& last) {
875
  return with_bit_iterator_adapter<AlgoFunc>(bit_iterator(first), last);
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 bit_iterator<SrcIt>& first,
881
    const DstIt& last) {
882
  return with_bit_iterator_adapter<AlgoFunc>(first, bit_iterator(last));
883
}
884

885
namespace detail {
886

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

892
template <typename size_type, bool resizeable, std::size_t Extent>
893
struct container_size_storage {
894
  constexpr size_type size() const noexcept {
302✔
895
    return Extent;
302✔
896
  }
151✔
897

898
  constexpr container_size_storage() noexcept {}
297✔
899
};
900

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

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

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

920
}  // namespace detail
921

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