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

PeterCDMcLean / BitLib / 14919535413

09 May 2025 01:36AM UTC coverage: 96.956% (-0.08%) from 97.036%
14919535413

Pull #8

github

web-flow
Merge 708ead0cd into a837637fd
Pull Request #8: Common bit array base

36 of 38 new or added lines in 2 files covered. (94.74%)

4 existing lines in 3 files now uncovered.

1274 of 1314 relevant lines covered (96.96%)

16656585.79 hits per line

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

94.29
/include/bitlib/bit-containers/bit_array.hpp
1
// ================================= BIT_ARRAY =================================== //
2
// Project:     The Experimental Bit Algorithms Library
3
// \file        bit_array.hpp
4
// Description: Implementation of bit_array
5
// Creator:     Vincent Reverdy
6
// Contributor: Peter McLean [2025]
7
// License:     BSD 3-Clause License
8
// ========================================================================== //
9
#ifndef _BIT_ARRAY_HPP_INCLUDED
10
#define _BIT_ARRAY_HPP_INCLUDED
11
// ========================================================================== //
12

13

14

15
// ================================ PREAMBLE ================================ //
16
// C++ standard library
17
#include <algorithm>
18
#include <bit>
19
#include <cmath>
20
#include <span>
21
#include <string>
22
#include <type_traits>
23
#include <vector>
24
#include <cstring> // memcpy
25

26
// Project sources
27
#include "bitlib/bit-algorithms/bit_algorithm.hpp"
28
#include "bitlib/bit-containers/bit_array_base.hpp"
29
#include "bitlib/bit-containers/bit_bitsof.hpp"
30
#include "bitlib/bit-containers/bit_span.hpp"
31
#include "bitlib/bit-iterator/bit.hpp"
32

33
namespace bit {
34
// ========================================================================== //
35

36
namespace detail {
37

38
template <typename value_type, typename word_type, std::size_t N>
39
constexpr size_t Words() { return (N * bitsof<value_type>() + bitsof<word_type>() - 1) / bitsof<word_type>(); }
40

41
template <typename value_type, typename word_type, std::size_t N>
42
using bit_array_d_it = typename std::conditional<std::is_same_v<value_type, bit_value>,
43
                                                 bit_iterator<typename std::array<word_type, Words<value_type, word_type, N>()>::iterator>,
44
                                                 typename std::array<word_type, Words<value_type, word_type, N>()>::iterator>::type;
45

46
template <typename value_type, typename word_type, std::size_t N>
47
using bit_array_d_cit = typename std::conditional<std::is_same_v<value_type, bit_value>,
48
                                                  bit_iterator<typename std::array<word_type, Words<value_type, word_type, N>()>::const_iterator>,
49
                                                  typename std::array<const word_type, Words<value_type, word_type, N>()>::const_iterator>::type;
50
}  // namespace detail
51

52
template <typename T = bit_value,
53
          std::size_t N = std::dynamic_extent,
54
          std::align_val_t V = std::align_val_t(alignof(T)),
55
          typename W = std::conditional_t<std::is_same_v<T, bit_value>, uint8_t, T>>
56
class bit_array : public bit_array_base<bit_array<T, N, V, W>, T, W, detail::bit_array_d_it<T, W, N>, detail::bit_array_d_cit<T, W, N>> {
57
 public:
58
  using typename bit_array_base<bit_array<T, N, V, W>, T, W, detail::bit_array_d_it<T, W, N>, detail::bit_array_d_cit<T, W, N>>::word_type;
59
  using typename bit_array_base<bit_array<T, N, V, W>, T, W, detail::bit_array_d_it<T, W, N>, detail::bit_array_d_cit<T, W, N>>::value_type;
60
  using typename bit_array_base<bit_array<T, N, V, W>, T, W, detail::bit_array_d_it<T, W, N>, detail::bit_array_d_cit<T, W, N>>::size_type;
61
  using typename bit_array_base<bit_array<T, N, V, W>, T, W, detail::bit_array_d_it<T, W, N>, detail::bit_array_d_cit<T, W, N>>::difference_type;
62
  using typename bit_array_base<bit_array<T, N, V, W>, T, W, detail::bit_array_d_it<T, W, N>, detail::bit_array_d_cit<T, W, N>>::reference;
63
  using typename bit_array_base<bit_array<T, N, V, W>, T, W, detail::bit_array_d_it<T, W, N>, detail::bit_array_d_cit<T, W, N>>::const_reference;
64
  using typename bit_array_base<bit_array<T, N, V, W>, T, W, detail::bit_array_d_it<T, W, N>, detail::bit_array_d_cit<T, W, N>>::pointer;
65
  using typename bit_array_base<bit_array<T, N, V, W>, T, W, detail::bit_array_d_it<T, W, N>, detail::bit_array_d_cit<T, W, N>>::const_pointer;
66
  using typename bit_array_base<bit_array<T, N, V, W>, T, W, detail::bit_array_d_it<T, W, N>, detail::bit_array_d_cit<T, W, N>>::iterator;
67
  using typename bit_array_base<bit_array<T, N, V, W>, T, W, detail::bit_array_d_it<T, W, N>, detail::bit_array_d_cit<T, W, N>>::const_iterator;
68

69
  static constexpr std::size_t bits = N * bitsof<T>();
70

71
 private:
72
  static constexpr std::size_t Words = (N * bitsof<value_type>() + bitsof<word_type>() - 1) / bitsof<word_type>();
73
  static constexpr std::size_t AlignedWords = (((Words * sizeof(word_type) + static_cast<size_t>(V) - 1) & ~(static_cast<size_t>(V) - 1)) + sizeof(word_type) - 1) / sizeof(word_type);
74

75
  alignas(static_cast<size_t>(V)) std::array<word_type, AlignedWords> storage;
76

77
 public:
78
  //using iterator = typename std::conditional<std::is_same_v<T, bit_value>,
79
  //                                         bit_iterator<typename std::array<word_type, Words>::iterator>,
80
  //                                         typename std::array<word_type, Words>::iterator>::type;
81
  //using const_iterator = typename std::conditional<std::is_same_v<T, bit_value>,
82
  //                                               bit_iterator<typename std::array<word_type, Words>::const_iterator>,
83
  //                                               typename std::array<const word_type, Words>::const_iterator>::type;
84
  /*
85
  * Constructors, copies and moves...
86
  */
87
  constexpr bit_array() noexcept;
88
  constexpr bit_array(value_type bit_val);
89
  template <std::integral U>
90
  constexpr bit_array(const U& integral) requires (bitsof<U>() <= bits);
91
  constexpr bit_array(const bit_array<T, N, V, W>& other) = default;
92
  constexpr bit_array(const bit_array<T, N, V, W>&& other) noexcept;
93
  constexpr bit_array(const std::initializer_list<value_type> init)
94
    requires(!std::is_same_v<value_type, word_type>);
95
  constexpr bit_array(const std::initializer_list<bool> init);
96
  constexpr bit_array(const std::initializer_list<word_type> init);
97
  constexpr bit_array(const std::string_view s)
98
    requires(std::is_same_v<value_type, bit_value>);
99

100
  ~bit_array() = default;
101
  /*
102
    * Assignment
103
    */
104
  constexpr bit_array& operator=(const bit_array<T, N, V, W>& other) = default;
105
  constexpr bit_array& operator=(bit_array<T, N, V, W>&& other) noexcept;
106

107
  // Using base class operator==
108

109
  /*
110
    * Element Access - using base class implementations
111
    */
112
  constexpr word_type* data() noexcept;
113
  constexpr const word_type* data() const noexcept;
114

115
  /*
116
    * Iterators
117
    */
118
  constexpr iterator begin() noexcept;
119
  constexpr iterator end() noexcept;
120
  constexpr const_iterator begin() const noexcept;
121
  constexpr const_iterator end() const noexcept;
122
  constexpr const_iterator cbegin() const noexcept;
123
  constexpr const_iterator cend() const noexcept;
124

125
  /*
126
    * Capacity
127
    */
128
  constexpr size_type size() const noexcept;
129

130
  /*
131
    * Slice
132
    */
133
  // Slice operators are provided by the base class
134

135
  /*
136
    * Operations
137
    */
138
  // Common operations
139
  // Fill is provided by the base class
140
  constexpr void swap(bit_array<T, N, V, W>& other) noexcept;
141
  template <std::integral U>
142
  explicit constexpr operator U() const noexcept requires (bitsof<U>() >= (bitsof<T>() * N));
143

144
  // String representation inherited from base
145
  // debug_string is provided by the base class
146
};
147

148
static_assert(bit_range<bit_array<bit_value, 11>>, "bit_array does not satisfy bit_range concept!");
149
static_assert(bit_sized_range<bit_array<bit_value, 11>>, "bit_array does not satisfy bit_sized_range concept!");
150
#ifdef CONTIGUOUS_RANGE
151
static_assert(bit_contiguous_range<bit_array<11>>, "bit_array does not satisfy bit_contiguous_range concept!");
152
static_assert(bit_contiguous_sized_range<bit_array<11>>, "bit_array does not satisfy bit_contiguous_sized_range concept!");
153
#endif
154

155
#if 0
156
// Class Template Argument Deduction
157
// CTAD guide for the constructor taking a word_type&,
158
// deducing Extent as bitsof<word_type>().
159
template <typename word_type>
160
bit_array(word_type&) -> bit_array<word_type, bitsof<word_type>()>;
161
template <typename word_type>
162
bit_array(word_type*) -> bit_array<word_type, bitsof<word_type>()>;
163

164
// CTAD guide for the constructor taking a word_type* and a size,
165
// which should always be used when Extent is std::dynamic_extent.
166
template <typename word_type>
167
bit_array(word_type&, std::size_t) -> bit_array<word_type, std::dynamic_extent>;
168
template <typename word_type>
169
bit_array(word_type*, std::size_t) -> bit_array<word_type, std::dynamic_extent>;
170
#endif
171

172
template <typename T, std::size_t N, std::align_val_t V, typename W>
173
constexpr bit_array<T, N, V, W>::bit_array() noexcept : storage{} {}
2✔
174

175
template <typename T, std::size_t N, std::align_val_t V, typename W>
176
constexpr bit_array<T, N, V, W>::bit_array(bit_array<T, N, V, W>::value_type bit_val) : storage{} {
177
  this->fill(bit_val);
178
}
179

180
template <typename T, std::size_t N, std::align_val_t V, typename W>
181
template <std::integral U>
182
constexpr bit_array<T, N, V, W>::bit_array(const U& integral) requires (bitsof<U>() <= bit_array<T, N, V, W>::bits) {
183
  std::memcpy(&storage[0], &integral, sizeof(integral));
184

185
  bool sign_extend = false;
186
  if constexpr (std::is_signed_v<U>) {
187
    sign_extend = (integral & (1 << (bitsof<U>()-1))) ? true : false;
188
  }
189
  if (sign_extend) {
190
    for (auto it = begin()+bitsof<U>(); it != end(); ++it) {
191
      *it = bit1;
192
    }
193
  } else {
194
    for (auto it = begin()+bitsof<U>(); it != end(); ++it) {
195
      *it = bit0;
196
    }
197
  }
198
}
199

200
// fill method is now provided by the base class
201

202
template <typename T, std::size_t N, std::align_val_t V, typename W>
203
constexpr bit_array<T, N, V, W>::bit_array(const std::initializer_list<value_type> init)
6✔
204
  requires(!std::is_same_v<value_type, word_type>)
205
{
206
  if(init.size() != bitsof(*this)) [[unlikely]] {
6✔
207
    throw std::invalid_argument("initialize_list contains an invalid number of bits for bit_array.");
×
208
  }
209
  std::copy(init.begin(), init.end(), this->begin());
6✔
210
}
6✔
211

212
template <typename T, std::size_t N, std::align_val_t V, typename W>
213
constexpr bit_array<T, N, V, W>::bit_array(const std::initializer_list<bool> init) {
214
  if(init.size() != bitsof(*this)) [[unlikely]] {
215
    throw std::invalid_argument("initialize_list contains an invalid number of bits for bit_array.");
216
  }
217
  std::copy(init.begin(), init.end(), this->begin());
218
}
219

220
template <typename T, std::size_t N, std::align_val_t V, typename W>
221
constexpr bit_array<T, N, V, W>::bit_array(const std::initializer_list<word_type> init) : storage{} {
222
  // Make sure we handle the case where init.size() != Words
223
  auto it = init.begin();
224
  for (size_type i = 0; i < std::min(AlignedWords, init.size()); ++i, ++it) {
225
    storage[i] = *it;
226
  }
227
}
228

229
template <typename T, std::size_t N, std::align_val_t V, typename W>
230
constexpr bit_array<T, N, V, W>::bit_array(const std::string_view s)
1✔
231
  requires(std::is_same_v<value_type, bit_value>)
232
{
233
  if (bitsof(*this) != static_cast<size_t>(std::count(s.begin(), s.end(), '0') + std::count(s.begin(), s.end(), '1'))) [[unlikely]] {
1✔
UNCOV
234
    throw std::invalid_argument("String contains an invalid number of bits for bit_array.");
×
235
  };
236
  size_type i = 0;
1✔
237
  for (char c : s) {
14✔
238
    if (c == '0') {
13✔
239
      begin()[i++] = bit0;
5✔
240
    } else if (c == '1') {
8✔
241
      begin()[i++] = bit1;
6✔
242
    }
243
  }
244
}
1✔
245

246
template <typename T, std::size_t N, std::align_val_t V, typename W>
247
constexpr bit_array<T, N, V, W>::bit_array(const bit_array<T, N, V, W>&& other) noexcept
1✔
248
    : storage(other.storage) {}
1✔
249

250
template <typename T, std::size_t N, std::align_val_t V, typename W>
251
constexpr bit_array<T, N, V, W>& bit_array<T, N, V, W>::operator=(bit_array<T, N, V, W>&& other) noexcept {
1✔
252
  std::copy(other.storage.begin(), other.storage.end(), storage.begin());
1✔
253
  return *this;
1✔
254
}
255

256
template <typename T, std::size_t N, std::align_val_t V, typename W>
257
constexpr void bit_array<T, N, V, W>::swap(bit_array<T, N, V, W>& other) noexcept {
1✔
258
  std::swap(this->storage, other.storage);
1✔
259
}
1✔
260

261
// -------------------------------------------------------------------------- //
262
/*
263
    * Element Access - data pointers
264
    */
265
template <typename T, std::size_t N, std::align_val_t V, typename W>
266
constexpr typename bit_array<T, N, V, W>::word_type* bit_array<T, N, V, W>::data() noexcept {
1✔
267
  return size() ? storage.data() : nullptr;
2✔
268
}
269

270
template <typename T, std::size_t N, std::align_val_t V, typename W>
271
constexpr const typename bit_array<T, N, V, W>::word_type* bit_array<T, N, V, W>::data() const noexcept {
272
  return size() ? storage.data() : nullptr;
273
}
274

275
// -------------------------------------------------------------------------- //
276

277
template <typename T, std::size_t N, std::align_val_t V, typename W>
278
constexpr typename bit_array<T, N, V, W>::size_type bit_array<T, N, V, W>::size() const noexcept { return N; }
182✔
279

280
// Iterators
281
// -------------------------------------------------------------------------- //
282

283
template <typename T, std::size_t N, std::align_val_t V, typename W>
284
constexpr typename bit_array<T, N, V, W>::iterator bit_array<T, N, V, W>::begin() noexcept {
121✔
285
  return iterator(storage.begin());
121✔
286
}
287

288
template <typename T, std::size_t N, std::align_val_t V, typename W>
289
constexpr typename bit_array<T, N, V, W>::iterator bit_array<T, N, V, W>::end() noexcept {
15✔
290
  return begin() + size();
15✔
291
}
292

293
template <typename T, std::size_t N, std::align_val_t V, typename W>
294
constexpr typename bit_array<T, N, V, W>::const_iterator bit_array<T, N, V, W>::begin() const noexcept {
13✔
295
  return const_iterator(storage.begin());
13✔
296
}
297

298
template <typename T, std::size_t N, std::align_val_t V, typename W>
299
constexpr typename bit_array<T, N, V, W>::const_iterator bit_array<T, N, V, W>::end() const noexcept {
11✔
300
  return const_iterator(storage.begin()) + size();
11✔
301
}
302

303
template <typename T, std::size_t N, std::align_val_t V, typename W>
304
constexpr typename bit_array<T, N, V, W>::const_iterator bit_array<T, N, V, W>::cbegin() const noexcept {
305
  return const_iterator(storage.begin());
306
}
307

308
template <typename T, std::size_t N, std::align_val_t V, typename W>
309
constexpr typename bit_array<T, N, V, W>::const_iterator bit_array<T, N, V, W>::cend() const noexcept {
310
  return const_iterator(storage.begin()) + size();
311
}
312

313
// -------------------------------------------------------------------------- //
314

315
// Slice operators are now provided by the base class
316

317
template <typename T, std::size_t N, std::align_val_t V, typename W>
318
template <std::integral U>
319
constexpr bit_array<T, N, V, W>::operator U() const noexcept
320
  requires(bitsof<U>() >= (bitsof<T>() * N))
321
{
322
  U result;
323
  std::memcpy(&result, &storage[0], sizeof(U));
324

325
  if constexpr (std::is_signed_v<U> && begin()[size()-1]) {
326
    for (size_type i = size(); i < bitsof<U>(); ++i) {
327
      result |= (static_cast<U>(1) << i);
328
    }
329
  } else {
330
    for (size_type i = size(); i < bitsof<U>(); ++i) {
331
      result &= ~(static_cast<U>(1) << i);
332
    }
333
  }
334
  return result;
335
}
336

337
// ========================================================================== //
338
template <char Bit>
339
constexpr void _parameter_pack_base_bits_prefix_len(size_t& base, size_t& bits, size_t& prefix_len, uint64_t& num) {
340
  if ('\'' == Bit) {
341
    if (0 != bits) {
342
      return;  //skip. Should we, though? It seems like it will be confusing
343
    }
344
    bits = num;
345
    num = 0;
346
    ++prefix_len;
347
    if (0 == base) {
348
      base = 10;
349
    }
350
  } else if (0 == base) {
351
    if (Bit == 'b' || Bit == 'B') {
352
      base = 2;
353
      num = 0;
354
    } else if (Bit == 'x' || Bit == 'X') {
355
      base = 16;
356
      num = 0;
357
    } else if (Bit == 'd' || Bit == 'D') {
358
      base = 10;
359
      num = 0;
360
    } else {
361
      num = (num * 10) + (Bit - '0');
362
    }
363
    ++prefix_len;
364
  } else {
365
    uint8_t decimal;
366
    switch (base) {
367
      case 2:
368
        decimal = static_cast<uint8_t>(Bit - '0');
369
        break;
370
      case 10:
371
        decimal = static_cast<uint8_t>(Bit - '0');
372
        break;
373
      case 16:
374
        decimal = static_cast<uint8_t>(Bit - '0');
375
        if (Bit >= 'a' && Bit <= 'f') {
376
          decimal = static_cast<uint8_t>(Bit - 'a') + 10u;
377
        }
378
        if (Bit >= 'A' && Bit <= 'F') {
379
          decimal = static_cast<uint8_t>(Bit - 'A') + 10u;
380
        }
381
        break;
382
    }
383
    num = num * base + decimal;
384
  }
385
}
386

387
template <char Bit, char... Bits>
388
constexpr void _parameter_pack_base_bits_prefix_len(size_t& base, size_t& bits, size_t& prefix_len, uint64_t& num)
389
  requires(sizeof...(Bits) > 0)
390
{
391
  _parameter_pack_base_bits_prefix_len<Bit>(base, bits, prefix_len, num);
392
  _parameter_pack_base_bits_prefix_len<Bits...>(base, bits, prefix_len, num);
393
}
394

395
template <char... Bits>
396
constexpr std::pair<size_t, uint64_t> _parameter_pack_decode_prefixed_num() {
397
  size_t base = 0;
398
  size_t bits = 0;
399
  size_t prefix_len = 0;
400
  uint64_t num = 0;
401
  _parameter_pack_base_bits_prefix_len<Bits...>(base, bits, prefix_len, num);
402
  size_t digits = (sizeof...(Bits) - prefix_len);
403
  if (0 == base) {
404
    base = 10;
405
    digits = prefix_len;
406
    prefix_len = 0;
407
  }
408
  if (0 == bits) {
409
    if (base == 2) {
410
      bits = digits;
411
    } else if (base == 10) {
412
      bits = std::bit_width(num);
413
    } else {
414
      bits = 4 * digits;
415
    }
416
  }
417
  return {bits, num};
418
}
419

420
} // namespace bit
421

422
template <char... Str>
423
constexpr bit::bit_array<bit::bit_value, bit::_parameter_pack_decode_prefixed_num<Str...>().first> operator""_b() {
424
  bit::bit_array<bit::bit_value, bit::_parameter_pack_decode_prefixed_num<Str...>().first> arr{};
425
  auto [bits, num] = bit::_parameter_pack_decode_prefixed_num<Str...>();
426
  for (int i = 0; i < arr.size(); i++) {
427
    arr[i] = (num & 0x1) ? bit::bit1 : bit::bit0;
428
    num >>= 1;
429
  }
430
  return arr;
431
}
432

433
#endif // _BIT_ARRAY_HPP_INCLUDED
434
       // ========================================================================== //
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

© 2025 Coveralls, Inc