1 #ifndef MARNAV__UTILS__BITSET__HPP
2 #define MARNAV__UTILS__BITSET__HPP
4 /// Copyright (c) 2017 Mario Konrad <mario.konrad@gmx.net>
5 /// The code is licensed under the BSD License (see file LICENSE)
18 /// This is a dynamically growing bitset (theoretically of arbitrary size).
20 /// Bits are stored in blocks, whose data type is configurable.
22 /// This may not be the most performant implementation, but a very flexible one.
24 /// This class suffers the same problems as std::vector<bool> does. Although is
25 /// provides an interator-like facility, it is not std algorithm compatible, nor
26 /// claims nor wants to be.
28 /// @tparam Block The data type of the underlying block type.
30 /// **Example:** appending individual bits
32 /// bitset<uint32_t> bits;
33 /// bits.append(0xff, 8); // append 8 bits
34 /// bits.append(0xff, 6); // append 6 bits
35 /// bits.append(1, 1); // append 1 bit
36 /// bits.append(1, 1); // append 1 bit
37 /// auto result = bits.get<uint16_t>(0); // since all bits were 1, this will be 0xffff
40 /// **Example:** random access to bits
42 /// bitset<uint32_t> bits;
44 /// auto bit = bits[7]; // indexed access (note: read only, unchecked)
45 /// bits.set(0xff, 3, 2); // set 2 bits (11) at offset 3
48 /// **Example:** bitset with initial number of bits
50 /// bitset<uint8_t> bits(1024);
51 /// bits.set(1, 512, 1); // set one bit to 1 at offset 512
54 template <class Block
,
55 class = typename
std::enable_if
<!std::numeric_limits
<Block
>::is_signed
>::type
>
59 using block_type
= Block
;
61 /// It is assumed, that a byte has 8 bits. This template will
62 /// not work on other architectures.
63 static constexpr auto bits_per_byte
= 8u;
65 static constexpr auto bits_per_block
= sizeof(block_type
) * bits_per_byte
;
68 using container
= std::vector
<block_type
>;
69 using size_type
= typename
container::size_type
;
70 using data_const_iterator
= typename
container::const_iterator
;
72 /// This is a non-std conform const iterator. The intention is
73 /// to provide a basic iterator functionality without claiming
74 /// to be std compliant.
80 using difference_type
= long long;
81 using value_type
= bool;
82 using iterator_category
= std::random_access_iterator_tag
;
85 const bitset
* bs
; ///< Associated bitset.
86 size_type pos
; ///< Position (bit) within the bitset.
89 const_iterator(const bitset
* const b
, size_type p
)
102 const_iterator(const const_iterator
&) = default;
103 const_iterator(const_iterator
&&) noexcept
= default;
105 size_type
get_pos() const { return pos
; }
107 const_iterator
& operator=(const const_iterator
&) = default;
108 const_iterator
& operator=(const_iterator
&&) noexcept
= default;
110 bool operator==(const const_iterator
& other
) const noexcept
112 return bs
== other
.bs
&& pos
== other
.pos
;
115 bool operator!=(const const_iterator
& other
) const noexcept
117 return bs
!= other
.bs
|| pos
!= other
.pos
;
120 bool operator<(const const_iterator
& other
) const noexcept
122 return bs
== other
.bs
&& pos
< other
.pos
;
125 bool operator>(const const_iterator
& other
) const noexcept
127 return bs
== other
.bs
&& pos
> other
.pos
;
130 bool operator<=(const const_iterator
& other
) const noexcept
132 return bs
== other
.bs
&& pos
<= other
.pos
;
135 bool operator>=(const const_iterator
& other
) const noexcept
137 return bs
== other
.bs
&& pos
>= other
.pos
;
140 bool operator*() const
142 return bs
!= nullptr && pos
< bs
->size() && bs
->get_bit(pos
) == true;
145 const_iterator
operator+(size_type n
) const noexcept
147 return const_iterator
{*this} += n
;
150 const_iterator
operator-(size_type n
) const noexcept
152 return const_iterator
{*this} -= n
;
155 const_iterator
& operator+=(size_type ofs
)
157 if (bs
!= nullptr && pos
< bs
->size()) {
159 if (pos
> bs
->size())
165 const_iterator
& operator-=(size_type ofs
)
168 pos
= (ofs
> pos
) ? 0 : pos
- ofs
;
173 const_iterator
& operator++() // ++const_iterator
175 if (bs
!= nullptr && pos
< bs
->size()) {
181 const_iterator
& operator--() // --const_iterator
183 if (bs
!= nullptr && pos
> 0) {
189 const_iterator
operator++(int) // const_iterator++
191 const_iterator
res(*this);
192 if (bs
!= nullptr && pos
< bs
->size()) {
198 const_iterator
operator--(int) // const_iterator--
200 const_iterator
res(*this);
201 if (bs
!= nullptr && pos
> 0) {
207 /// Peeks from the bitset, but does not advance the iterator.
208 template <typename T
> void peek(T
& v
, size_type bits
= sizeof(T
) * bits_per_byte
) const
212 if (pos
+ bits
> bs
->size())
213 throw std::out_of_range
{
214 "out of range, not possible to peek beyond available bits"};
215 bs
->get(v
, pos
, bits
);
218 /// Reads from the bitset and advances the iterator the number of read bits.
219 template <typename T
> void read(T
& v
, size_type bits
= sizeof(T
) * bits_per_byte
)
227 size_type pos
; // number of bits contained within the set
231 /// Extends the container by the specified number of bits.
232 /// Extension is always one block.
233 void extend(size_type bits
)
236 size_type n_blocks
= (pos
+ bits
+ bits_per_block
- 1) / bits_per_block
;
237 if (n_blocks
> data
.size()) {
238 data
.reserve(n_blocks
);
239 while (bits
> capacity() - pos
) {
240 data
.push_back(block_type
{});
245 /// Appends the specified block to the data. The bitset is automatically extended
246 /// to hold all the data.
248 /// @param[in] v The data to append to the bitset.
249 /// @param[in] bits The number of bits to append. This value must be
250 /// between 0 and bits_per_block. If not all bits are being
251 /// appended, only the least significant bits are being taken.
252 void append_block(block_type v
, size_type bits
= bits_per_block
)
256 size_type i
= pos
/ bits_per_block
; // index of current block
258 // number of bits unused within the current block
259 size_type u_bits
= bits_per_block
- (pos
% bits_per_block
);
261 if (u_bits
>= bits
) {
262 // enough room within current block
263 data
[i
] |= v
<< (u_bits
- bits
);
265 // not enough room, split value to current and next block
266 data
[i
+ 0] |= v
>> (bits
- u_bits
);
267 data
[i
+ 1] |= v
<< (bits_per_block
- (bits
- u_bits
));
272 /// Sets the specified block within the bit set. The bitset is automatically
273 /// extended to hold the data.
275 /// @param[in] v The data to be set.
276 /// @param[in] ofs The offset in bits at which the data has to be written.
277 /// @param[in] bits The number of bits of the data to be written
278 /// If not all bits are being written, only the least significant bits are being
280 /// @note This function does not extend the data container. It is assumed, that
281 /// this function runs in the context of another, which takes care of the
282 /// container (e.g. set_impl). This behaviour is intentional, because
283 /// this function runs potentially often, and therefore it is desired to
284 /// avoid additional runtime checks which can easily be done in the calling
286 void set_block(block_type v
, size_type ofs
, size_type bits
= bits_per_block
)
288 size_type i
= ofs
/ bits_per_block
; // index of current block
290 // number of bits unused within the current block
291 size_type u_bits
= bits_per_block
- (ofs
% bits_per_block
);
293 if (u_bits
>= bits
) {
294 // enough room within current block
295 block_type mask0
= -1;
297 block_type mask1
= -1;
298 mask1
<<= u_bits
- bits
;
302 data
[i
] = (data
[i
] & mask0
) | v
;
304 // not enough room, split value to current and next block
305 block_type mask0
= ~((1 << (bits
- u_bits
)) - 1);
306 block_type mask1
= (1 << (bits_per_block
- (bits
- u_bits
))) - 1;
308 data
[i
+ 0] = (data
[i
+ 0] & mask0
) | v
>> (bits
- u_bits
);
309 data
[i
+ 1] = (data
[i
+ 1] & mask1
) | v
<< (bits_per_block
- (bits
- u_bits
));
311 if (ofs
+ bits
> pos
)
315 template <typename T
>
316 void set_impl(T v
, size_type ofs
, size_type bits
= sizeof(T
) * bits_per_byte
)
319 if (bits
> sizeof(v
) * bits_per_byte
)
320 throw std::invalid_argument
{"number of bit exceed number of available bits"};
321 if (ofs
+ bits
> capacity())
322 extend(ofs
+ bits
- capacity());
324 // fraction of the first block
325 size_type u_bits
= bits_per_block
- (ofs
% bits_per_block
); // part of the first block
327 if (bits
<= u_bits
) {
328 set_block(v
, ofs
, bits
);
332 set_block(v
>> (bits
- u_bits
), ofs
, u_bits
);
338 // all complete blocks
339 for (; bits
> bits_per_block
; bits
-= bits_per_block
, ofs
+= bits_per_block
) {
340 set_block(v
>> (bits
- bits_per_block
), ofs
);
343 // fraction of the last block
345 set_block(v
<< (bits_per_block
- bits
), ofs
);
349 template <typename T
> void set_dispatch(T v
, size_type ofs
, size_type bits
, std::true_type
)
351 set_impl(v
, ofs
, bits
);
354 template <typename T
> void set_dispatch(T v
, size_type ofs
, size_type bits
, std::false_type
)
356 set_impl(static_cast<typename
std::underlying_type
<T
>::type
>(v
), ofs
, bits
);
359 /// Reads a block from the bit set.
361 /// @return The container to hold the data.
362 /// @param[in] ofs The offset in bits at which the data has to be read.
363 /// @param[in] bits Number of bits to be read.
364 /// If the number of bits is smaller than what the specified data can
365 /// hold, only the least significant bits are being set.
366 /// @exception std::out_of_range There are not enough bits to read, offset and number
367 /// of bits exceed the total number of available bits. It is not possible
368 /// to read past the end.
369 block_type
get_block(size_type ofs
, size_type bits
= bits_per_block
) const noexcept
372 const size_type i
= ofs
/ bits_per_block
; // index of current block
374 // number of bits unused within the current block
375 const size_type u_bits
= bits_per_block
- (ofs
% bits_per_block
);
377 if (u_bits
>= bits
) {
378 // desired data fully within the current block
379 block_type mask
= (1 << u_bits
) - 1;
380 return (data
[i
] & mask
) >> (u_bits
- bits
);
382 // desired value is part from current block and part from next
383 block_type mask0
= (1 << u_bits
) - 1;
384 block_type mask1
= ((1 << (bits_per_block
- (bits
- u_bits
))) - 1)
386 return (data
[i
+ 0] & mask0
) << (bits
- u_bits
)
387 | (data
[i
+ 1] & mask1
) >> (bits_per_block
- (bits
- u_bits
));
391 /// Copies a block from source to destination offsets.
393 /// This is the equivalent of `set_block(get_block(...), ...)`
394 void copy_block(size_type r_ofs
, size_type w_ofs
, size_type bits
= bits_per_block
)
396 set_block(get_block(r_ofs
, bits
), w_ofs
, bits
);
399 public: // constructors
401 bitset(const bitset
&) = default;
403 /// Move constructor.
404 bitset(bitset
&&) = default;
407 bitset
& operator=(const bitset
&) = default;
410 bitset
& operator=(bitset
&&) = default;
412 /// Default construction
418 /// Construction with number of bits present (and usable) in the bitset.
419 /// All bits are initialized to the default constructed Block type..
421 /// @param[in] bits Number of bits (default initialized to 0) in the bitset.
422 explicit bitset(size_type bits
)
429 /// Construction with content from a specified container.
431 /// @param[in] begin Start position of the data (inclusive)
432 /// @param[in] end End position of the data (exclusive)
433 bitset(typename
container::const_iterator begin
, typename
container::const_iterator end
)
434 : pos((end
- begin
) * bits_per_block
)
439 /// Construction with move of the container, this does not copy any data.
440 explicit bitset(container
&& container
)
441 : pos(container
.size() * bits_per_block
)
442 , data(std::move(container
))
446 /// Constructs a bitset from the specified range.
448 /// It tries to copy blockwise.
449 bitset(const_iterator first
, const_iterator last
)
457 const size_type distance
= last
.pos
- first
.pos
;
458 const size_type n_blocks
= distance
/ bits_per_block
;
459 const size_type u_bits
= distance
% bits_per_block
;
462 data
.reserve(n_blocks
+ 1);
464 data
.reserve(n_blocks
);
467 size_type r_ofs
= first
.pos
;
468 for (size_type i
= 0; i
< n_blocks
; ++i
, r_ofs
+= bits_per_block
)
469 data
.push_back(first
.bs
->get_block(r_ofs
, bits_per_block
));
470 pos
= n_blocks
* bits_per_block
;
473 append(first
.bs
->get_block(r_ofs
, u_bits
), u_bits
);
476 /// Constructs a bitset using the provided blocks.
477 bitset(std::initializer_list
<Block
> init_list
)
480 data
.assign(init_list
);
481 pos
= data
.size() * bits_per_block
;
484 public: // container operations
485 /// Returns the capacity of this bit set. Note: not all bits must have
487 size_type
capacity() const noexcept
{ return data
.size() * bits_per_block
; }
489 /// Returns the number of used bits.
490 size_type
size() const noexcept
{ return pos
; }
492 /// Reserves the number of blocks within this set.
494 /// @note This method reserves blocks, not bits.
495 void reserve(size_type blocks
) { extend(blocks
* bits_per_block
); }
497 /// Clears the bit set.
505 /// Returns a const iterator to the beginning of the data itself.
506 /// Note: this iterator accesses the data up to capacity(), some bits
507 /// may be unused at the end of the set.
508 data_const_iterator
data_begin() const { return data
.begin(); }
510 /// Returns a const iterator to the end of the data itself.
511 data_const_iterator
data_end() const { return data
.end(); }
513 const_iterator
begin() const { return const_iterator(this, 0); }
515 const_iterator
end() const { return const_iterator(this, size()); }
517 const_iterator
cbegin() const { return const_iterator(this, 0); }
519 const_iterator
cend() const { return const_iterator(this, size()); }
522 /// Appends another bitset to this one.
524 /// @param[in] bs The bitset to be appended to this one.
526 /// @note It is not allowed to append a bitself to itself.
527 /// @note This algorithm is not efficient.
528 template <class U
> void append(const bitset
<U
> & bs
)
530 if (reinterpret_cast<const void *>(this) == reinterpret_cast<const void *>(&bs
))
532 for (const auto && bit
: bs
)
536 /// Appends the lowest significant bits of the specified data to the
537 /// bit set. The set will be extended if necessary.
538 /// The second parameter specifies the number of bits to be used from
539 /// the given data, beginning at the lowest signnificant bit.
540 /// A size of 0 bits will have no effect.
542 /// @param[in] v The value to append to the bitset.
543 /// @param[in] bits Number of bits from the specified value. This must not
544 /// exceed the number of bits provided by the specified data,
545 /// padding is not supported.
546 /// @exception std::invalid_argument Number of bites exceed the number of
547 /// bit provided by the parameter v. padding is not implemented.
548 /// Example: type of v is uint32_t, bits is 40.
549 template <typename T
> void append(T v
, size_type bits
= sizeof(T
) * bits_per_byte
)
553 if (bits
> sizeof(v
) * bits_per_byte
)
554 throw std::invalid_argument
{"number of bits exceed number of available bits"};
555 size_type n_bits
= bits
% bits_per_block
; // incomplete blocks
557 append_block(v
>> (bits
- n_bits
), n_bits
);
560 for (; bits
> 0; bits
-= bits_per_block
) {
561 append_block(v
>> (bits
- bits_per_block
));
566 /// Sets the specified bitset at the offset within this bitset.
568 /// @param[in] bs The bitset to copy.
569 /// @param[in] ofs The offset within the bitset to copy the bitset
570 /// to. The entire specified bitset will be set.
572 /// @note It is not allowed to set a bitself to itself.
573 /// @note This algorithm is not efficient.
574 template <class U
> void set(const bitset
<U
> & bs
, size_type ofs
)
576 if (reinterpret_cast<const void *>(this) == reinterpret_cast<const void *>(&bs
))
578 for (const auto & bit
: bs
) {
584 /// Sets bits within the set. The bitset is automatically exteneded to hold the data.
586 /// @param[in] v The value to set.
587 /// @param[in] ofs The offset (in bits) at which position the value has to be written.
588 /// @param[in] bits The number of bits to write. This must not exceed the number of bits
589 /// provided by the specified data, padding is not supported.
590 /// @exception std::invalid_argument Number of bites exceed the number of
591 /// bit provided by the parameter v. padding is not implemented.
592 /// Example: type of v is uint32_t, bits is 40.
593 template <typename T
>
594 void set(T v
, size_type ofs
, size_type bits
= sizeof(T
) * bits_per_byte
)
596 set_dispatch(v
, ofs
, bits
, std::is_integral
<T
>{});
599 /// Resets the bit at the speficied index.
600 void reset(size_type index
)
603 throw std::out_of_range
{"index out of range in reset(index)"};
607 /// Sets all bits within the bitset to 0.
608 void reset() noexcept
610 for (auto & block
: data
)
614 /// Sets a single bit at a specified position to the specified value.
615 /// This method accesses the bitset as direct as possible and therefore does not
616 /// extend the set if the index is out of range.
618 /// @exception std::out_of_range Specified index is out of range.
619 void set_bit(size_type i
, bool value
)
622 throw std::out_of_range
{"index out of range"};
624 // bit within the block to be read
625 size_type n_bit
= bits_per_block
- (i
% bits_per_block
) - 1;
627 data
[i
/ bits_per_block
] |= (1u << n_bit
);
629 data
[i
/ bits_per_block
] &= ~(1u << n_bit
);
634 /// Returns the bit at the specified position. If the index is larger
635 /// than the actual number of bits, 'false' will rturn.
637 /// @exception std::out_of_range Specified index is out of range.
638 bool get_bit(size_type i
) const
641 throw std::out_of_range
{"index out of range"};
643 // bit within the block to be read
644 size_type n_bit
= bits_per_block
- (i
% bits_per_block
) - 1;
645 return ((data
[i
/ bits_per_block
] >> n_bit
) & 1) ? true : false;
648 /// Simply an other name for get_bit.
649 bool test(size_type i
) const { return get_bit(i
); }
651 /// Reads data from the bit set. There must be enough capacity in either the
652 /// bitset to be read as well as the provided data type to contain the desired
655 /// @param[in] ofs The offset in bits at which position the data is to be read.
656 /// @param[in] bits Number of bits to be read. This must not exceeed the number of
657 /// bits the specified data type can hold.
658 /// If the number of bits is smaller than what the specified data can
659 /// hold, only the least significant bits are being set.
660 /// @return The data read from the bitset.
661 /// @exception std::invalid_argument Number of bites exceed the number of
662 /// bit provided by the return value. padding is not implemented.
663 /// Example: return value is of type uint32_t, bits is 40.
664 /// @exception std::out_of_range Offset and bits exceed the number of available
665 /// bits. It is not possible to read beyond the end.
667 typename
std::enable_if
<std::is_integral
<T
>::value
&& !std::is_same
<T
, bool>::value
,
669 get(size_type ofs
, size_type bits
= sizeof(T
) * bits_per_byte
) const
673 if (bits
> sizeof(T
) * bits_per_byte
)
674 throw std::invalid_argument
{"number of bits (" + std::to_string(bits
)
675 + ") exceed number of available bits ("
676 + std::to_string(sizeof(T
) * bits_per_byte
) + ")"};
677 if (ofs
+ bits
> pos
)
678 throw std::out_of_range
{"offset (" + std::to_string(ofs
) + ") and bits ("
679 + std::to_string(bits
) + ") exceed available number of bits ("
680 + std::to_string(pos
) + ")"};
684 // number of bits unused within the current block
685 size_type u_bits
= bits_per_block
- (ofs
% bits_per_block
);
687 // fraction of the first block
689 auto block
= get_block(ofs
, u_bits
);
691 block
>>= (u_bits
- bits
);
700 // all complete blocks inbetween, only possible if sizeof(T) exceeds
701 // the block size (mupltiple blocks in one T).
702 // since this check is possible at compile time, modern compilers
703 // probably will eliminated it completely.
704 if (sizeof(T
) * bits_per_byte
> bits_per_block
) {
705 for (; bits
>= bits_per_block
; bits
-= bits_per_block
) {
706 value
<<= bits_per_block
;
707 value
+= get_block(ofs
);
708 ofs
+= bits_per_block
;
712 // fraction of the last block
715 value
+= get_block(ofs
, bits
);
721 /// Specialization of `get` for enumerations.
723 typename
std::enable_if
<std::is_enum
<T
>::value
, T
>::type
get(
724 size_type ofs
, size_type bits
= sizeof(T
) * bits_per_byte
) const
726 return static_cast<T
>(get
<typename
std::underlying_type
<T
>::type
>(ofs
, bits
));
729 /// Specialization of `get` for bool.
730 template <class T
, typename
std::enable_if
<std::is_same
<T
, bool>::value
, int>::type
= 0>
731 void get(T
& value
, size_type ofs
, size_type
= 1) const
733 value
= get_bit(ofs
);
736 template <class T
, typename
std::enable_if
<!std::is_same
<T
, bool>::value
, int>::type
= 0>
737 void get(T
& value
, size_type ofs
, size_type bits
= sizeof(T
) * bits_per_byte
) const
739 value
= get
<T
>(ofs
, bits
);
742 bool get(size_type ofs
) const { return get_bit(ofs
); }
744 public: // access operators
745 /// Returns the bit at the specified position.
746 bool operator[](size_type i
) const { return get_bit(i
); }
748 public: // comparison operators
749 /// Equality comparison operator for the same bitset type.
750 bool operator==(const bitset
& other
) const { return this == &other
|| data
== other
.data
; }
752 /// Inequality comparison operator for the same bitset type.
753 bool operator!=(const bitset
& other
) const { return !(*this == other
); }
755 /// Equalily comparison operator for different bitset types.
757 /// Since the blocks differ, a comparison bit by bit is done.
759 /// @note This is implemented for readablility, not max performance.
760 template <class XBlock
,
761 class = typename
std::enable_if
<!std::numeric_limits
<XBlock
>::is_signed
>::type
>
762 bool operator==(const bitset
<XBlock
> & other
) const
764 if (size() != other
.size())
768 auto j
= other
.begin();
778 /// Inequality comparison operator for different bitset types.
780 /// Since the blocks differ, a comparison bit by bit is done.
782 /// @note This is implemented for readablility, not max performance.
783 template <class XBlock
,
784 class = typename
std::enable_if
<!std::numeric_limits
<XBlock
>::is_signed
>::type
>
785 bool operator!=(const bitset
<XBlock
> & other
) const
787 return !(*this == other
);
790 /// Comparinson for 'less'.
791 bool operator<(const bitset
& other
) const
793 // are there any bits in the range of one bitset that exceeds the other
794 if (size() > other
.size()) {
795 if (any(begin() + (size() - other
.size()), end()))
797 } else if (other
.size() > size()) {
798 if (other
.any(other
.begin() + (other
.size() - size()), other
.end()))
802 // check all blocks from the most to the least significant.
803 for (auto i
= size() / bits_per_block
; i
> 0; --i
) {
804 if (data
[i
- 1] < other
.data
[i
- 1])
808 // no difference found, they are the same
812 /// Comparinson for 'less or equal'.
813 bool operator<=(const bitset
& other
) const
815 // are there any bits in the range of one bitset that exceeds the other
816 if (size() > other
.size()) {
817 if (any(begin() + (size() - other
.size()), end()))
819 } else if (other
.size() > size()) {
820 if (other
.any(other
.begin() + (other
.size() - size()), other
.end()))
824 // check all blocks from the most to the least significant.
825 for (auto i
= size() / bits_per_block
; i
> 0; --i
) {
826 if (data
[i
- 1] > other
.data
[i
- 1])
830 // no difference found, they are the same
834 /// Comparinson for 'greater'.
835 bool operator>(const bitset
& other
) const { return !(*this <= other
); }
837 /// Comparinson for 'greater or equal'.
838 bool operator>=(const bitset
& other
) const { return !(*this < other
); }
840 public: // arithmetic operators
841 /// Increments the bitset by one. If an overflow is to occurr, the bitset
842 /// resets to 0 and continues counting.
843 bitset
& operator++() // ++bitset
848 const size_type u_bits
= size() % bits_per_block
;
852 size_type ofs
= bits_per_block
* (size() / bits_per_block
);
854 block_type block
= get_block(ofs
, u_bits
);
856 if (block
< ((block_type
{1} << u_bits
) - 1)) {
858 set_block(block
, ofs
, u_bits
);
861 block
= block_type
{}; // maximum reached, overflow to the next block
862 set_block(block
, ofs
, u_bits
);
866 for (size_type i
= size() / bits_per_block
; i
> 0; --i
) {
867 if (data
[i
- 1] < std::numeric_limits
<block_type
>::max()) {
871 data
[i
- 1] = block_type
{};
877 bitset
operator++(int) // bitset++
879 bitset result
{*this};
884 /// Decrements the bitset by one. If an underflow is to occurr, the bitset
885 /// resets to all 1es and continues counting.
886 bitset
& operator--() // --bitset
891 const size_type u_bits
= size() % bits_per_block
;
895 size_type ofs
= bits_per_block
* (size() / bits_per_block
);
897 block_type block
= get_block(ofs
, u_bits
);
901 set_block(block
, ofs
, u_bits
);
904 block
= (block_type
{1} << u_bits
) - 1; // max reached, overflow to the next block
905 set_block(block
, ofs
, u_bits
);
909 for (size_type i
= size() / bits_per_block
; i
> 0; --i
) {
910 if (data
[i
- 1] > 0) {
914 data
[i
- 1] = std::numeric_limits
<block_type
>::max();
920 bitset
operator--(int) // bitset--
922 bitset result
{*this};
927 public: // shift operator
928 /// Bit shift left. This function tries to shift entire blocks at once.
929 bitset
& shl(size_type bits
)
931 if (bits
>= size()) {
936 // copy all bits necessary, block wise.
937 size_type r_ofs
= bits
;
939 while (r_ofs
< size()) {
941 // this resembles std::min, but std::min complains about
942 // bits_per_block being an undefined reference.
943 const size_type d
= size() - r_ofs
;
944 size_type u_bits
= d
< bits_per_block
? d
: bits_per_block
;
946 copy_block(r_ofs
, w_ofs
, u_bits
);
951 // overwrite all remaining bits with zeroes.
952 while (w_ofs
< size()) {
954 // same issue with std::min here.
955 const size_type d
= size() - w_ofs
;
956 size_type u_bits
= d
< bits_per_block
? d
: bits_per_block
;
958 set_block(0, w_ofs
, u_bits
);
959 w_ofs
+= bits_per_block
;
965 bitset
& operator<<=(size_type bits
) { return shl(bits
); }
967 bitset
operator<<(size_type bits
) const { return bitset
{*this} <<= bits
; }
969 /// Bit shift right. This function tries to shift entire blocks
971 bitset
& shr(size_type bits
)
973 if (bits
>= size()) {
981 // first: handle the last bits (probably partially a block, or a single block)
982 size_type u_bits
= size() % bits_per_block
;
984 if (u_bits
== size()) {
985 u_bits
= size() - bits
;
989 r_ofs
= size() - u_bits
- bits
;
990 w_ofs
= size() - u_bits
;
993 if (size() == bits_per_block
) {
994 u_bits
= bits_per_block
- bits
;
998 u_bits
= bits_per_block
;
999 r_ofs
= size() - bits_per_block
- bits
;
1000 w_ofs
= size() - bits_per_block
;
1003 copy_block(r_ofs
, w_ofs
, u_bits
);
1005 // second: handle whole blocks
1007 if (r_ofs
> bits_per_block
) {
1008 u_bits
= bits_per_block
;
1009 r_ofs
-= bits_per_block
;
1010 w_ofs
-= bits_per_block
;
1016 copy_block(r_ofs
, w_ofs
, u_bits
);
1019 // third: reset all bits remaining in front
1021 u_bits
= (w_ofs
< bits_per_block
) ? w_ofs
: bits_per_block
;
1023 set_block(0, w_ofs
, u_bits
);
1029 bitset
& operator>>=(size_type bits
) { return shr(bits
); }
1031 bitset
operator>>(size_type bits
) const { return bitset
{*this} >>= bits
; }
1033 public: // logic operator
1034 /// Logical or operation. The resulting bitset is of the size of the larger one.
1036 /// Example: '0011' | '11000' = '11110'
1037 bitset
& operator|=(const bitset
& other
)
1039 if (size() < other
.size()) {
1040 extend(other
.size() - size());
1044 for (auto i
= 0u; i
< data
.size(); ++i
)
1045 data
[i
] |= other
.data
[i
];
1050 bitset
operator|(const bitset
& other
) const { return bitset
{*this} |= other
; }
1052 /// Logical or operation. The resulting bitset is of the size of the smaller one.
1054 /// Example: '0111' & '11001' = '0100'
1055 bitset
& operator&=(const bitset
& other
)
1057 if (size() > other
.size()) {
1058 for (auto i
= other
.size(); i
< size(); ++i
)
1063 for (auto i
= 0u; i
< data
.size(); ++i
)
1064 data
[i
] &= other
.data
[i
];
1069 bitset
operator&(const bitset
& other
) const { return bitset
{*this} &= other
; }
1071 /// Logical xor operation. The resulting bitset is of the size of the larger one.
1073 /// Example: '0011' ^ '11100' = '11011'
1074 bitset
& operator^=(const bitset
& other
)
1076 if (size() < other
.size()) {
1077 extend(other
.size() - size());
1081 for (auto i
= 0u; i
< data
.size(); ++i
)
1082 data
[i
] ^= other
.data
[i
];
1087 bitset
operator^(const bitset
& other
) const { return bitset
{*this} ^= other
; }
1090 /// Flips the bit at the specified index.
1092 /// @exception std::out_of_range Specified index is out of range.
1093 void flip(size_type i
) { set(!get_bit(i
), i
, 1); }
1095 /// Returns true if all bits are true.
1097 /// @note This is implemented for readablility, not max performance.
1098 bool all() const noexcept
1100 for (auto i
= begin(); i
!= end(); ++i
)
1106 /// Returns true if any of the bits are true.
1108 /// @note This is implemented for readablility, not max performance.
1109 bool any() const noexcept
{ return any(begin(), end()); }
1111 /// Returns true if any of the bits in the specified range are true.
1113 /// @note This is implemented for readablility, not max performance.
1114 bool any(const_iterator first
, const_iterator last
) const noexcept
1116 for (auto i
= first
; i
!= last
; ++i
)
1122 /// Returns true if none of the bits are true.
1124 /// @note This is implemented for readablility, not max performance.
1125 bool none() const noexcept
{ return none(begin(), end()); }
1127 /// Returns true if none of the bits within the specified range are true.
1129 /// @note This is implemented for readablility, not max performance.
1130 bool none(const_iterator first
, const_iterator last
) const noexcept
1132 for (auto i
= first
; i
!= last
; ++i
)
1138 /// Returns the number of bits set to true.
1140 /// @note This is implemented for readablility, not max performance.
1141 size_type
count() const noexcept
{ return count(begin(), end()); }
1143 /// Returns the number of bits between the specified iterators.
1145 /// @param[in] first Ponints to the first bit of the range to test.
1146 /// @param[in] last Ponits to the bit after the range.
1148 /// @note This is implemented for readablility, not max performance.
1149 size_type
count(const_iterator first
, const_iterator last
) const noexcept
1152 for (auto i
= first
; i
!= last
; ++i
)