7
7
#include < atomic>
8
8
#include < cassert>
9
9
10
- template <size_t BIT_COUNT>
11
- struct min_fit_int {
12
- using type = std::conditional_t <BIT_COUNT <= 8 , uint8_t ,
13
- std::conditional_t <BIT_COUNT <= 16 , uint16_t ,
14
- std::conditional_t <BIT_COUNT <= 32 , uint32_t ,
15
- std::conditional_t <BIT_COUNT <= 64 , uint64_t ,
16
- void >>>>;
17
- };
18
-
19
10
template <bool SET, typename T>
20
11
constexpr bool set_bit_atomic (std::atomic<T>& data, size_t index) {
21
12
T old_val;
@@ -37,12 +28,28 @@ constexpr bool set_bit_atomic(std::atomic<T>& data, size_t index) {
37
28
template <size_t N>
38
29
class atomic_bitset {
39
30
private:
40
- using internal_type = typename min_fit_int<N>::type ;
31
+ std::array<std::atomic< uint64_t >, N / 64 + (N % 64 ? 1 : 0 )> data ;
41
32
42
- // TODO: Is it preferable to always have an array of bytes since it gives greater granularity to the atomics?
43
- std::conditional_t <N <= 64 , std::atomic<internal_type>, std::array<std::atomic<uint8_t >, N / 8 + (N % 8 ? 1 : 0 )>> data;
33
+ static inline thread_local std::random_device dev;
34
+ static inline thread_local std::minstd_rand rng{ dev () };
35
+ static inline thread_local std::uniform_int_distribution dist{ 0 , static_cast <int >(N - 1 ) };
36
+
37
+ template <bool IS_SET>
38
+ static constexpr size_t claim_bit_singular (std::atomic<uint64_t >& data) {
39
+ auto initial_rot = dist (rng);
40
+ auto rotated = std::rotr<uint64_t >(data, initial_rot);
41
+ int counted;
42
+ for (int i = 0 ; i < 64 ; i += counted) {
43
+ counted = IS_SET ? std::countr_zero (rotated) : std::countr_one (rotated);
44
+ auto original_index = (initial_rot + i + counted) % 64 ;
45
+ if (set_bit_atomic<!IS_SET>(data, original_index)) {
46
+ return original_index;
47
+ }
48
+ rotated >>= ++counted;
49
+ }
44
50
45
- static constexpr bool uses_array = N > 64 ;
51
+ return std::numeric_limits<size_t >::max ();
52
+ }
46
53
47
54
public:
48
55
static constexpr size_t size () { return N; }
@@ -54,11 +61,7 @@ class atomic_bitset {
54
61
// / <returns>Whether the bit has been newly set. false means the bit had already been set.</returns>
55
62
constexpr bool set (size_t index) {
56
63
assert (index < size ());
57
- if constexpr (uses_array) {
58
- return set_bit_atomic<true >(data[index / 8 ], index % 8 );
59
- } else if constexpr (!uses_array) {
60
- return set_bit_atomic<true >(data, index );
61
- }
64
+ return set_bit_atomic<true >(data[index / 64 ], index % 64 );
62
65
}
63
66
64
67
// / <summary>
@@ -68,37 +71,35 @@ class atomic_bitset {
68
71
// / <returns>Whether the bit has been newly set. false means the bit had already been set.</returns>
69
72
constexpr bool reset (size_t index) {
70
73
assert (index < size ());
71
- if constexpr (uses_array) {
72
- return set_bit_atomic<false >(data[index / 8 ], index % 8 );
73
- } else if constexpr (!uses_array) {
74
- return set_bit_atomic<false >(data, index );
75
- }
74
+ return set_bit_atomic<false >(data[index / 64 ], index % 64 );
76
75
}
77
76
78
77
constexpr bool test (size_t index, std::memory_order order = std::memory_order_seq_cst) const {
79
78
assert (index < size ());
80
- if constexpr (uses_array) {
81
- return data[index / 8 ].load (order) & (1 << (index % 8 ));
82
- } else if constexpr (!uses_array) {
83
- return data.load (order) & (1ull << index );
84
- }
79
+ return data[index / 64 ].load (order) & (1ull << (index % 64 ));
85
80
}
86
81
87
82
constexpr bool operator [](size_t index) const {
88
83
return test (index );
89
84
}
90
85
91
86
constexpr bool any () const {
92
- if constexpr (uses_array) {
93
- for (auto & elem : data) {
94
- if (elem) {
95
- return true ;
96
- }
87
+ for (auto & elem : data) {
88
+ if (elem) {
89
+ return true ;
90
+ }
91
+ }
92
+ return false ;
93
+ }
94
+
95
+ template <bool IS_SET>
96
+ constexpr size_t claim_bit () {
97
+ for (size_t i = 0 ; i < data.size (); i++) {
98
+ if (auto ret = claim_bit_singular<IS_SET>(data[i]); ret != std::numeric_limits<size_t >::max ()) {
99
+ return ret + i * 64 ;
97
100
}
98
- return false ;
99
- } else {
100
- return data;
101
101
}
102
+ return std::numeric_limits<size_t >::max ();
102
103
}
103
104
};
104
105
0 commit comments