ATLAS Offline Software
Loading...
Searching...
No Matches
ConcurrentBitset.icc
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4/**
5 * @file CxxUtils/ConcurrentBitset.icc
6 * @author scott snyder <snyder@bnl.gov>
7 * @date Nov, 2017
8 * @brief Variable-sized bitset allowing (mostly) concurrent access.
9 */
10
11
12namespace CxxUtils {
13
14
15//*********************************************************************************
16// Constructors, destructors, assignment.
17
18
19/**
20 * @brief The number of bits that this container can hold.
21 */
22inline
23ConcurrentBitset::bit_t ConcurrentBitset::capacity() const
24{
25 return (*m_impl).nbits();
26}
27
28
29/**
30 * @brief Count the number of 1 bits in the set.
31 */
32inline
33ConcurrentBitset::bit_t ConcurrentBitset::count() const
34{
35 return (*m_impl).count();
36}
37
38
39/**
40 * @brief Count the number of 1 bits in the set.
41 *
42 * Note: If you regard this like a std::bitset, you would expect this to return
43 * the number of bits that the set can hold, while if you regard this like
44 * a set<bit_t>, then you would expect this to return the number of 1 bits.
45 * We follow the latter here.
46 */
47inline
48ConcurrentBitset::bit_t ConcurrentBitset::size() const
49{
50 return (*m_impl).count();
51}
52
53
54/**
55 * @brief Test to see if a bit is set.
56 * @param bit Number of the bit to test.
57 * @return true if the bit is set; false otherwise.
58 *
59 * Returns false if @c bit is beyond the end of the set.
60 */
61inline
62bool ConcurrentBitset::test (bit_t bit) const
63{
64 return (*m_impl).test (bit);
65}
66
67/**
68 * @brief Test to see if a bit is set.
69 * @param bit Number of the bit to test.
70 * @return 1 if the bit is set; 0 otherwise.
71 *
72 * Returns 0 if @c bit is beyond the end of the set.
73 */
74inline
75size_t ConcurrentBitset::count (bit_t bit) const
76{
77 return test (bit);
78}
79
80
81/**
82 * @brief Return true if there are no 1 bits in the set.
83 */
84inline
85bool ConcurrentBitset::empty() const
86{
87 return none();
88}
89
90
91/**
92 * @brief Return true if there are no 1 bits in the set.
93 */
94inline
95bool ConcurrentBitset::none() const
96{
97 return (*m_impl).none();
98}
99
100
101/**
102 * @brief Return true if all bits in the set are 1.
103 */
104inline
105bool ConcurrentBitset::all() const
106{
107 return (*m_impl).all();
108}
109
110
111/**
112 * @brief Return true if there are any 1 bits in the set.
113 */
114inline
115bool ConcurrentBitset::any() const
116{
117 return !none();
118}
119
120
121//*********************************************************************************
122// Single-bit manipulation.
123
124
125/**
126 * @brief Turn on one bit.
127 * @param bit The bit to turn on.
128 *
129 * Does nothing if @c bit beyond the end of the set.
130 */
131inline
132ConcurrentBitset& ConcurrentBitset::set (bit_t bit)
133{
134 (*m_impl).set (bit);
135 return *this;
136}
137
138
139/**
140 * @brief Turn off one bit.
141 * @param bit The bit to turn off.
142 *
143 * Does nothing if @c bit beyond the end of the set.
144 */
145inline
146ConcurrentBitset& ConcurrentBitset::reset (bit_t bit)
147{
148 (*m_impl).reset (bit);
149 return *this;
150}
151
152
153/**
154 * @brief Turn off one bit.
155 * @param bit The bit to turn off.
156 *
157 * Does nothing if @c bit beyond the end of the set.
158 */
159inline
160ConcurrentBitset& ConcurrentBitset::erase (bit_t bit)
161{
162 (*m_impl).reset (bit);
163 return *this;
164}
165
166
167/**
168 * @brief Flip the value of one bit.
169 * @param bit The bit to turn flip.
170 *
171 * Does nothing if @c bit beyond the end of the set.
172 */
173inline
174ConcurrentBitset& ConcurrentBitset::flip (bit_t bit)
175{
176 (*m_impl).flip (bit);
177 return *this;
178}
179
180
181/**
182 * @brief Set the value of one bit.
183 * @param bit The bit to turn set.
184 * @param val The value to which to set it.
185 *
186 * Does nothing if @c bit beyond the end of the set.
187 */
188inline
189ConcurrentBitset& ConcurrentBitset::set (bit_t bit, bool val)
190{
191 if (val) {
192 (*m_impl).set (bit);
193 }
194 else {
195 (*m_impl).reset (bit);
196 }
197 return *this;
198}
199
200
201//*********************************************************************************
202// Set operations.
203
204
205/**
206 * @brief Clear all bits in the set.
207 *
208 * This operation is not necessarily atomic; a simultaneous read may be able
209 * to see the operation partially done.
210 */
211inline
212ConcurrentBitset& ConcurrentBitset::clear()
213{
214 (*m_impl).clear();
215 return *this;
216}
217
218
219/**
220 * @brief Clear all bits in the set.
221 *
222 * This operation is not necessarily atomic; a simultaneous read may be able
223 * to see the operation partially done.
224 */
225inline
226ConcurrentBitset& ConcurrentBitset::reset()
227{
228 (*m_impl).clear();
229 return *this;
230}
231
232
233/**
234 * @brief Turn on all bits in the set.
235 *
236 * This operation is not necessarily atomic; a simultaneous read may be able
237 * to see the operation partially done.
238 */
239inline
240ConcurrentBitset& ConcurrentBitset::set()
241{
242 (*m_impl).set();
243 return *this;
244}
245
246/**
247 * @brief Flip the state of all bits in the set.
248 *
249 * This operation is not necessarily atomic; a simultaneous read may be able
250 * to see the operation partially done.
251 */
252inline
253ConcurrentBitset& ConcurrentBitset::flip()
254{
255 (*m_impl).flip();
256 return *this;
257}
258
259
260/**
261 * @brief AND this set with another set.
262 * @param other The other set.
263 *
264 * This operation is not necessarily atomic; a simultaneous read may be able
265 * to see the operation partially done.
266 */
267inline
268ConcurrentBitset& ConcurrentBitset::operator&= (const ConcurrentBitset& other)
269{
270 (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a &= v; },
271 *other.m_impl);
272 return *this;
273}
274
275
276/**
277 * @brief OR this set with another set.
278 * @param other The other set.
279 *
280 * This operation is not necessarily atomic; a simultaneous read may be able
281 * to see the operation partially done.
282 */
283inline
284ConcurrentBitset& ConcurrentBitset::operator|= (const ConcurrentBitset& other)
285{
286 (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a |= v; },
287 *other.m_impl);
288 return *this;
289}
290
291
292/**
293 * @brief XOR this set with another set.
294 * @param other The other set.
295 *
296 * This operation is not necessarily atomic; a simultaneous read may be able
297 * to see the operation partially done.
298 */
299inline
300ConcurrentBitset& ConcurrentBitset::operator^= (const ConcurrentBitset& other)
301{
302 (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a ^= v; },
303 *other.m_impl);
304 return *this;
305}
306
307
308/**
309 * @brief Subtract another set from this set.
310 * @param other The other set.
311 *
312 * This is the same as (*this) &= ~other;
313 *
314 * This operation is not necessarily atomic; a simultaneous read may be able
315 * to see the operation partially done.
316 */
317inline
318ConcurrentBitset& ConcurrentBitset::operator-= (const ConcurrentBitset& other)
319{
320 (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a &= ~v; },
321 *other.m_impl);
322 return *this;
323}
324
325
326/**
327 * @brief Return a new set that is the complement of this set.
328 */
329inline
330ConcurrentBitset ConcurrentBitset::operator~() const
331{
332 ConcurrentBitset b = *this;
333 (*b.m_impl).flip();
334 return b;
335}
336
337
338//*********************************************************************************
339// Comparison.
340
341
342/**
343 * @brief Test two sets for equality.
344 * @param other The other set to test.
345 */
346inline
347bool ConcurrentBitset::operator== (const ConcurrentBitset& other) const
348{
349 return (*m_impl) == (*other.m_impl);
350}
351
352
353/**
354 * @brief Test two sets for inequality.
355 * @param other The other set to test.
356 */
357inline
358bool ConcurrentBitset::operator!= (const ConcurrentBitset& other) const
359{
360 return ! ((*m_impl) == (*other.m_impl));
361}
362
363
364//*********************************************************************************
365// Insert.
366
367
368/**
369 * @brief Set a bit to 1. Expand the set if needed.
370 * @param bit Number of the bit to set.
371 * @param new_nbits Hint for new size of set, if it needs to be expanded.
372 *
373 * If @c bit is past the end of the container, then the container will be
374 * expanded as needed.
375 *
376 * This operation is incompatible with any other simultaneous writes
377 * to the same set (reads are ok).
378 */
379inline
380ConcurrentBitset& ConcurrentBitset::insert (bit_t bit, bit_t new_nbits /*= 0*/)
381{
382 if (bit >= (*m_impl).nbits()) {
383 if (new_nbits > bit)
384 ;
385 else if (bit < 64)
386 new_nbits = 128;
387 else
388 new_nbits = (bit * 2 + BLOCKSIZE-1) & ~(BLOCKSIZE-1);
389 expand (new_nbits);
390 }
391 set (bit);
392 return *this;
393}
394
395
396/**
397 * @brief Set several bits to 1. Expand the set if needed.
398 * @param beg Start of range of bits to set.
399 * @param end End of range of bits to set.
400 * @param new_nbits Hint for new size of set, if it needs to be expanded.
401 *
402 * The iteration range should be over something convertible to @c bit_t.
403 * If any bit is past the end of the container, then the container will be
404 * expanded as needed.
405 *
406 * This operation is incompatible with any other simultaneous writes
407 * to the same set (reads are ok).
408 *
409 * Example:
410 *@code
411 * std::vector<bit_t> bits { 4, 10, 12};
412 * CxxUtils::ConcurrentBitset bs = ...;
413 * bs.insert (bits.begin(), bits.end());
414 @endcode
415*/
416template <class ITERATOR,
417 typename /*= typename std::enable_if<
418 std::is_base_of<
419 typename std::forward_iterator_tag,
420 typename std::iterator_traits<ITERATOR>::iterator_category
421 >::value>*/ >
422inline
423ConcurrentBitset&
424ConcurrentBitset::insert (ITERATOR beg, ITERATOR end, bit_t new_nbits /*= 0*/)
425{
426 for (; beg != end; ++beg) {
427 insert (*beg, new_nbits);
428 }
429 return *this;
430}
431
432
433/**
434 * @brief Set several bits to 1. Expand the set if needed.
435 * @param l List of bits to set.
436 * @param new_nbits Hint for new size of set, if it needs to be expanded.
437 *
438 * If any bit is past the end of the container, then the container will be
439 * expanded as needed.
440 *
441 * This operation is incompatible with any other simultaneous writes
442 * to the same set (reads are ok).
443 *
444 * Example:
445 *@code
446 * std::vector<bit_t> bits { 4, 10, 12};
447 * bs.insert ({4, 10, 12});
448 @endcode
449*/
450inline
451ConcurrentBitset&
452ConcurrentBitset::insert (std::initializer_list<bit_t> l, bit_t new_nbits /*= 0*/)
453{
454 auto max_it = std::max_element (l.begin(), l.end());
455 size_t bmax = max_it == l.end() ? 0 : *max_it + 1;
456 if (new_nbits > bmax) new_nbits = bmax;
457 expand (bmax);
458 for (size_t x : l) {
459 set (x);
460 }
461 return *this;
462}
463
464
465/**
466 * @brief Turn on bits listed in another set.
467 * @param other Set of bits to turn on.
468 *
469 * This is the same as @c operator|=, except that if the size of @c other is
470 * larger than this set, then this set will be expanded to match @c other.
471 *
472 * This operation is incompatible with any other simultaneous writes
473 * to the same set (reads are ok).
474 */
475inline
476ConcurrentBitset& ConcurrentBitset::insert (const ConcurrentBitset& other)
477{
478 const Impl* otherImpl = other.m_impl;
479 expand (otherImpl->nbits());
480 (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a |= v; },
481 *otherImpl);
482 return *this;
483}
484
485
486//*********************************************************************************
487// Array-like element access.
488
489
490/**
491 * @brief Constructor.
492 * @param impl ConcurrentBitset implementation object.
493 * @param bit Bit number to which this reference refers.
494 */
495inline
496ConcurrentBitset::reference::reference (Impl& impl, bit_t bit)
497 : m_block (impl.block(bit)),
498 m_mask (1UL << (bit&MASK))
499{
500}
501
502
503/**
504 * @brief Set the referenced bit to a given value.
505 * @param val Value to which to set the referenced bit.
506 */
507inline
508ConcurrentBitset::reference&
509ConcurrentBitset::reference::operator= (bool val) noexcept
510{
511 if (val)
512 *m_block |= m_mask;
513 else
514 *m_block &= ~m_mask;
515 return *this;
516}
517
518
519/**
520 * @brief Copy the value of another referenced bit.
521 * @param r The other reference.
522 *
523 * To allow:
524 *@code
525 * ConcurrentBitset& bs1 = ...;
526 * ConcurrentBitset& bs2 = ...;
527 * bs1[1] = bs2[1];
528 @endcode
529*/
530inline
531ConcurrentBitset::reference&
532ConcurrentBitset::reference::operator= (const reference& r) noexcept
533{
534 if (this != &r) {
535 *this = static_cast<bool> (r);
536 }
537 return *this;
538}
539
540
541/**
542 * @brief Return the value of the referenced bit.
543 */
544inline
545ConcurrentBitset::reference::operator bool() const noexcept
546{
547 return (*m_block & m_mask) != 0;
548}
549
550
551/**
552 * @brief Return the complement of the value of the referenced bit.
553 */
554inline
555bool ConcurrentBitset::reference::operator~() const noexcept
556{
557 return (*m_block & m_mask) == 0;
558}
559
560
561/**
562 * @brief Invert the referenced bit.
563 */
564inline
565ConcurrentBitset::reference&
566ConcurrentBitset::reference::flip() noexcept
567{
568 *m_block ^= m_mask;
569 return *this;
570}
571
572
573/**
574 * @brief Return the value of one bit.
575 * @param bit The number of the bit to test.
576 */
577inline
578bool ConcurrentBitset::operator[] (bit_t bit) const
579{
580 return (*m_impl).test (bit);
581}
582
583
584/**
585 * @brief Return a reference to one bit.
586 * @param bit The number of the bit to reference.
587 *
588 * The reference will be invalidated by calls to @c insert() or @c operator=.
589 * Effects are undefined if @c bit is past the end of the set.
590 */
591inline
592ConcurrentBitset::reference ConcurrentBitset::operator[] (bit_t bit)
593{
594 return reference (*m_impl, bit);
595}
596
597
598//*********************************************************************************
599// Iterator operations.
600
601
602/**
603 * @brief Constructor.
604 * @param cache Cached block at the current iteration point, shifted such
605 * that bit number @c bit has just been shifted off the right.
606 * @param bit Bit number the at which the iterator is currently pointing.
607 * @param data Pointer to the block at which the iterator is currently pointing.
608 * @param end One past the last block of the iteration range.
609 */
610inline
611ConcurrentBitset::const_iterator::const_iterator (Block_t cache,
612 bit_t bit,
613 const std::atomic<Block_t>* data,
614 const std::atomic<Block_t>* end)
615 : m_cache (cache), m_bit (bit), m_data(data), m_end (end)
616{
617}
618
619
620/**
621 * @brief Return the bit number which the iterator is currently referencing.
622 */
623inline
624ConcurrentBitset::bit_t
625ConcurrentBitset::const_iterator::operator*() const
626{
627 return m_bit;
628}
629
630
631/**
632 * @brief Advance the iterator to the next set bit.
633 */
634inline
635ConcurrentBitset::const_iterator&
636ConcurrentBitset::const_iterator::operator++()
637{
638 // Are there any more bits set in this block?
639 Block_t cache = m_cache;
640 if (cache != 0) [[likely]] {
641 // Yes --- find the first set bit.
642 // Shift until that bit just falls off the right and adjust
643 // the current position.
644 // We know that @c b will be less than @c BLOCKSIZE
645 // (avoiding undefined behavior), since at least one bit must have
646 // already been shifted off of the cache.
647 int b = std::countr_zero (cache)+1;
648 m_bit += b;
649 m_cache = cache >> b;
650 }
651 else {
652 // No, move to the next block.
653 // Bit number at the start of the next block.
654 unsigned int bit = (m_bit + BLOCKSIZE) & ~MASK;
655
656 // Pointers to the next block, and the end of iteration.
657 const std::atomic<Block_t>* data = m_data + 1;
658 const std::atomic<Block_t>* end = m_end;
659
660 // Iterate until we find a block with at least one bit set.
661 while (data < end) {
662 // Read the current block; test to see if there are any set bits.
663 cache = *data;
664 if (cache != 0) [[unlikely]] {
665 // Found a block with at least one bit set. Which is the first?
666 int b = std::countr_zero (cache);
667
668 // Adjust the current position.
669 m_bit = bit + b;
670 m_data = data;
671
672 // Shift the bit found off of cache.
673 // Need to do it in two steps, because @c b might be @c BLOCKSIZE-1
674 // (and shifting by @c BLOCKSIZE is undefined).
675 m_cache = (cache>>1) >> b;
676 return *this;
677 }
678
679 // Move to the next block.
680 ++data;
681 bit += BLOCKSIZE;
682 }
683
684 // Reached the end without finding any more bits set.
685 // Mark that we hit the end.
686 m_bit = ~static_cast<bit_t>(0);
687 }
688
689 return *this;
690}
691
692
693/**
694 * @brief Advance the iterator to the next set bit (postincrement).
695 */
696inline
697ConcurrentBitset::const_iterator
698ConcurrentBitset::const_iterator::operator++(int)
699{
700 const_iterator tmp = *this;
701 ++*this;
702 return tmp;
703}
704
705
706/**
707 * @brief Compare two iterators.
708 * @param other The other iterator to compare.
709 */
710inline
711bool ConcurrentBitset::const_iterator::operator== (const const_iterator& other) const
712{
713 return m_bit == other.m_bit;
714}
715
716
717/**
718 * @brief Compare two iterators.
719 * @param other The other iterator to compare.
720 */
721inline
722bool ConcurrentBitset::const_iterator::operator!= (const const_iterator& other) const
723{
724 return m_bit != other.m_bit;
725}
726
727
728/**
729 * @brief Return a begin iterator.
730 */
731inline
732ConcurrentBitset::const_iterator
733ConcurrentBitset::begin() const
734{
735 return (*m_impl).begin();
736}
737
738
739/**
740 * @brief Return an end iterator.
741 */
742inline
743ConcurrentBitset::const_iterator
744ConcurrentBitset::end() const
745{
746 return (*m_impl).end();
747}
748
749
750/**
751 * @brief If bit @c bit is set, return an iterator pointing to it.
752 * Otherwise, return an end iterator.
753 * @param bit Bit number to test.
754 */
755inline
756ConcurrentBitset::const_iterator
757ConcurrentBitset::find (bit_t bit) const
758{
759 return (*m_impl).find(bit);
760}
761
762
763//*********************************************************************************
764// Implementation.
765
766
767/**
768 * @brief Allocate an Impl structure.
769 * @param sz Size of an Impl structure.
770 * @param nbits Number of bits to allocate.
771 *
772 * The storage for the bits follows contiguously after the Impl structure itself
773 * (to save a memory reference); hence, we use a custom allocator.
774 */
775inline
776void* ConcurrentBitset::Impl::operator new (size_t /*sz*/,
777 ConcurrentBitset::bit_t nbits)
778{
779 bit_t nblocks = nBlocks (nbits);
780 // The Impl structure contains one Block_t at the end.
781 return malloc (sizeof(Impl) + (nblocks-1)*sizeof(Block_t));
782}
783
784
785/*
786 * @brief Free an Impl structure.
787 * @param p Pointer to the object to free.
788 */
789inline
790void ConcurrentBitset::Impl::operator delete (void* p)
791{
792 free (p);
793}
794
795
796/**
797 * @brief Find number of blocks needed to hold a given number of bits.
798 * @param nbits The number of bits.
799 */
800inline
801ConcurrentBitset::bit_t
802ConcurrentBitset::nBlocks (bit_t nbits)
803{
804 if (nbits == 0) return 1;
805 return (nbits+BLOCKSIZE-1) / BLOCKSIZE;
806}
807
808
809/**
810 * @brief Create a new, uninitialized implementation object.
811 * @param nbits Number of bits to allocate.
812 *
813 * This will allocate memory for the Impl object,
814 * but will does not run the constructor.
815 */
816inline
817ConcurrentBitset::Impl*
818ConcurrentBitset::newImpl (bit_t nbits)
819{
820 bit_t nblocks = nBlocks (nbits);
821 // The Impl structure contains one Block_t at the end.
822 return reinterpret_cast<Impl*>(malloc (sizeof(Impl) + (nblocks-1)*sizeof(Block_t)));
823}
824
825
826/**
827 * @brief Expand the container.
828 * @param new_nbits The desired new size of the container.
829 */
830inline
831void ConcurrentBitset::expand (bit_t new_nbits)
832{
833 // Check the size of the container.
834 // Call the out-of-line portion if we actually need to expand.
835 if (new_nbits > (*m_impl).nbits()) {
836 expandOol (new_nbits);
837 }
838}
839
840
841/**
842 * @brief Constructor.
843 * @param nbits Number of bits in the set.
844 */
845inline
846ConcurrentBitset::Impl::Impl (bit_t nbits)
847 : m_nbits (nbits),
848 m_nblocks (nBlocks (nbits))
849{
850 // Start with all bits 0.
851 clear();
852}
853
854
855/**
856 * @brief Copy constructor.
857 * @brief Other object to copy.
858 * @brief Number of bits to use for this container.
859 *
860 * If @c nbits is smaller than the size of @c other, then the size of @c other
861 * will be used instead.
862 */
863inline
864ConcurrentBitset::Impl::Impl (const Impl& other, bit_t nbits /*= 0*/)
865 : m_nbits (std::max (other.m_nbits, nbits)),
866 m_nblocks ((m_nbits+BLOCKSIZE-1) / BLOCKSIZE),
867 m_hwm (static_cast<size_t> (other.m_hwm))
868{
869 // Copy, then clear the remainder.
870 // We don't care about the relative ordering, so to this with relaxed
871 // memory ordering, and add a barrier at the end.
872 for (bit_t i=0; i < other.m_nblocks; i++) {
873 m_data[i].store (other.m_data[i].load(std::memory_order_relaxed),
874 std::memory_order_relaxed);
875 }
876 for (bit_t i=other.m_nblocks; i < m_nblocks; i++) {
877 m_data[i].store (0, std::memory_order_relaxed);
878 }
879 std::atomic_thread_fence (std::memory_order_seq_cst);
880}
881
882
883/**
884 * @brief Copy from another instance.
885 * @param other Object from which to copy.
886 *
887 * This does not change the size of the container.
888 * If This container is larger than @c other, then the remainder will be
889 * filled with zeros. If @c other is larger than this container, then the
890 * remainder will be ignored.
891 */
892inline
893void ConcurrentBitset::Impl::assign (const Impl& other)
894{
895 // Copy, then clear the remainder.
896 // We don't care about the relative ordering, so do this with relaxed
897 // memory ordering, and add a barrier at the end.
898 bit_t ncopy = std::min (m_nblocks, other.m_nblocks);
899 for (bit_t i=0; i < ncopy; i++) {
900 m_data[i].store (other.m_data[i].load(std::memory_order_relaxed),
901 std::memory_order_relaxed);
902 }
903 for (bit_t i=ncopy; i < m_nblocks; i++) {
904 m_data[i].store (0, std::memory_order_relaxed);
905 }
906 std::atomic_thread_fence (std::memory_order_seq_cst);
907
908 // Copy hwm last. It can only increase, so as long as we're after the barrier,
909 // we should get something that's correct.
910 m_hwm = static_cast<bit_t> (other.m_hwm);
911}
912
913
914/**
915 * @brief Return the number of bits in the set.
916 */
917inline
918ConcurrentBitset::bit_t ConcurrentBitset::Impl::nbits() const
919{
920 return m_nbits;
921}
922
923
924/**
925 * @brief Test to see if a bit is set.
926 * @param bit Number of the bit to test.
927 * @return true if the bit is set; false otherwise.
928 *
929 * Returns false if @c bit is beyond the end of the set.
930 */
931inline
932bool ConcurrentBitset::Impl::test (bit_t bit) const
933{
934 if (bit >= m_nbits) return false;
935 bit_t pos = bit / BLOCKSIZE;
936 return (m_data[pos] & (1UL<<(bit&MASK))) != 0;
937}
938
939
940/**
941 * @brief Return a pointer to the block containing @c bit.
942 * @param bit Desired bit number.
943 *
944 * Returns nullptr if @c bit is past the end of the set.
945 */
946inline
947std::atomic<ConcurrentBitset::Block_t>*
948ConcurrentBitset::Impl::block (bit_t bit)
949{
950 if (bit >= m_nbits) return nullptr;
951 bit_t pos = bit / BLOCKSIZE;
952 return &m_data[pos];
953}
954
955
956/**
957 * @brief Turn on one bit.
958 * @param bit The bit to turn on.
959 *
960 * Does nothing if @c bit beyond the end of the set.
961 */
962inline
963void ConcurrentBitset::Impl::set (bit_t bit)
964{
965 if (bit >= m_nbits) return;
966 bit_t pos = bit / BLOCKSIZE;
967 // Update HWM if pos is larger.
968 CxxUtils::atomic_fetch_max (&m_hwm, pos);
969 m_data[pos] |= 1UL<<(bit&MASK);
970}
971
972
973/**
974 * @brief Turn off one bit.
975 * @param bit The bit to turn off.
976 *
977 * Does nothing if @c bit beyond the end of the set.
978 */
979inline
980void ConcurrentBitset::Impl::reset (bit_t bit)
981{
982 if (bit >= m_nbits) return;
983 bit_t pos = bit / BLOCKSIZE;
984 m_data[pos] &= ~(1UL<<(bit&MASK));
985}
986
987
988/**
989 * @brief Flip the value of one bit.
990 * @param bit The bit to turn flip.
991 *
992 * Does nothing if @c bit beyond the end of the set.
993 */
994inline
995void ConcurrentBitset::Impl::flip (bit_t bit)
996{
997 if (bit >= m_nbits) return;
998 size_t pos = bit / BLOCKSIZE;
999 // Update HWM if pos is larger.
1000 CxxUtils::atomic_fetch_max (&m_hwm, pos);
1001 m_data[pos] ^= 1UL<<(bit&MASK);
1002}
1003
1004
1005/**
1006 * @brief Clear all bits in the set.
1007 *
1008 * This operation is not necessarily atomic; a simultaneous read may be able
1009 * to see the operation partially done.
1010 */
1011inline
1012void ConcurrentBitset::Impl::clear()
1013{
1014 for (bit_t i=0; i<m_nblocks; i++) {
1015 m_data[i].store (0, std::memory_order_relaxed);
1016 }
1017 std::atomic_thread_fence (std::memory_order_seq_cst);
1018 m_hwm = 0;
1019}
1020
1021
1022/**
1023 * @brief Apply a binary operation.
1024 * @param op Operation to apply.
1025 * @param other Second set for the operation.
1026 *
1027 * Each block B in this set is replaced by B OP OTHER, where OTHER is
1028 * the corresponding block in the other container. (If this set
1029 * is larger than @c other, then the trailing blocks will be 0.)
1030 */
1031inline
1032void ConcurrentBitset::Impl::operate (operator_t op, const Impl& other)
1033{
1034 bit_t nblocks = std::min (m_nblocks, other.m_nblocks);
1035 bit_t hwm = m_hwm;
1036 for (bit_t i = 0; i < nblocks; i++) {
1037 op (m_data[i], other.m_data[i]);
1038 if (m_data[i]) hwm = i;
1039 }
1040 for (bit_t i = nblocks; i < m_nblocks; i++) {
1041 op (m_data[i], static_cast<Block_t> (0));
1042 if (m_data[i]) hwm = i;
1043 }
1044 CxxUtils::atomic_fetch_max (&m_hwm, hwm);
1045}
1046
1047
1048/**
1049 * @brief Compare with another set.
1050 * @param other Other set with which to compare.
1051 */
1052inline
1053bool ConcurrentBitset::Impl::operator== (const Impl& other) const
1054{
1055 bit_t ntest = std::min (m_nblocks, other.m_nblocks);
1056 for (bit_t i = 0; i < ntest; i++) {
1057 if (m_data[i] != other.m_data[i]) return false;
1058 }
1059
1060 for (bit_t i = ntest; i < m_nblocks; i++) {
1061 if (m_data[i] != 0) return false;
1062 }
1063
1064 for (bit_t i = ntest; i < other.m_nblocks; i++) {
1065 if (other.m_data[i] != 0) return false;
1066 }
1067
1068 return true;
1069}
1070
1071
1072/**
1073 * @brief Return an iterator referencing the first 1 bit.
1074 */
1075inline
1076ConcurrentBitset::const_iterator
1077ConcurrentBitset::Impl::begin() const
1078{
1079 // Set the iterator to just before the start of the container.
1080 // Then use the increment operator to search for the first 1.
1081 bit_t offs = m_nblocks ? (static_cast<bit_t>(m_hwm) + 1) : 0;
1082 const_iterator it (0,
1083 static_cast<bit_t>(-BLOCKSIZE),
1084 &m_data[0] - 1,
1085 &m_data[0] + offs);
1086 ++it;
1087 return it;
1088}
1089
1090
1091/**
1092 * @brief Return the end iterator.
1093 */
1094inline
1095ConcurrentBitset::const_iterator
1096ConcurrentBitset::Impl::end() const
1097{
1098 return const_iterator (0,
1099 ~static_cast<bit_t>(0),
1100 nullptr, nullptr);
1101}
1102
1103
1104/**
1105 * @brief If bit @c bit is set, return an iterator pointing to it.
1106 * Otherwise, return an end iterator.
1107 * @param bit Bit number to test.
1108 */
1109inline
1110ConcurrentBitset::const_iterator
1111ConcurrentBitset::Impl::find (bit_t bit) const
1112{
1113 if (test (bit)) {
1114 // The bit's set.
1115 // Construct an iterator pointing at this bit.
1116 bit_t pos = bit / BLOCKSIZE;
1117 const std::atomic<Block_t>* data = m_data + pos;
1118 Block_t cache = *data;
1119 bit_t offs = bit&MASK;
1120 cache >>= 1;
1121 if (offs > 0) {
1122 cache >>= offs;
1123 }
1124 return const_iterator (cache, bit, m_data+pos,
1125 &m_data[0] + m_hwm + 1);
1126 }
1127 return end();
1128}
1129
1130
1131} // namespace CxxUtils