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