Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
StoredBitSet.hxx
Go to the documentation of this file.
1
35#ifndef _UTILS_STOREDBITSET_HXX_
36#define _UTILS_STOREDBITSET_HXX_
37
38#include <stdint.h>
39#include <string.h>
40
41#include "utils/macros.h"
42#include "utils/Atomic.hxx"
43
48{
49public:
54 virtual StoredBitSet &set_bit(unsigned offset, bool value) = 0;
55
59 virtual bool get_bit(unsigned offset) = 0;
60
69 unsigned offset, unsigned size, unsigned value) = 0;
70
77 virtual unsigned get_multi(unsigned offset, unsigned size) = 0;
78
81 virtual unsigned size() = 0;
82
85 virtual void flush() = 0;
86
88 virtual void lock_and_flush() = 0;
89};
90
91class ShadowedStoredBitSet : public StoredBitSet, protected Atomic
92{
93public:
94 StoredBitSet &set_bit(unsigned offset, bool value) override {
95 HASSERT(offset < size_);
96 auto* shadow_ptr = shadow_ + (offset >> 5);
97 auto bit = (UINT32_C(1) << (offset & 31));
98 if (value) {
99 if (__atomic_fetch_or(shadow_ptr, bit, __ATOMIC_SEQ_CST) & bit) {
100 return *this; // nothing to do
101 }
102 } else {
103 if ((__atomic_fetch_and(shadow_ptr, ~bit, __ATOMIC_SEQ_CST) & bit) == 0) {
104 return *this; // nothing to do
105 }
106 }
107 cell_offs_t d = offset / granularity_;
108 __atomic_fetch_or(dirty_ + (d >> 5), UINT32_C(1) << (d & 31), __ATOMIC_SEQ_CST);
110 return *this;
111 }
112
113 bool get_bit(unsigned offset) override {
114 HASSERT(offset < size_);
115 return shadow_[offset >> 5] & (UINT32_C(1) << (offset & 31));
116 }
117
119 unsigned offset, unsigned size, unsigned value) override {
120 HASSERT(size <= 32);
121 HASSERT(size > 0);
122 HASSERT(offset + size <= size_);
123 if (size < 32) {
124 HASSERT((value >> size) == 0);
125 }
126 unsigned sidx = offset >> 5;
127 uint32_t smask = UINT32_C(0xFFFFFFFF);
128 smask >>= (32 - size); // gets `size' count of one bits
129 smask <<= (offset & 31); // moves them to the right place
130 uint32_t sval = value;
131 sval <<= (offset & 31);
132 int rollover = size + (offset & 31) - 32;
133 if (rollover > 0) { // there are leftover bits for a second word
134 uint32_t smask2 = (UINT32_C(1) << rollover) - 1;
135 uint32_t sval2 = value >> (size - rollover);
136 // we need to atomically update two cells.
137 uint32_t old1 = shadow_[sidx], new1, old2 = shadow_[sidx + 1], new2;
138 do
139 {
140 new1 = (old1 & ~smask) | sval;
141 new2 = (old2 & ~smask2) | sval2;
142 } while (!__atomic_compare_exchange_n(shadow_ + sidx, &old1, new1,
143 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ||
144 !__atomic_compare_exchange_n(shadow_ + sidx + 1, &old2, new2,
145 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
146 }
147 else
148 {
149 // just one cell
150 uint32_t old1 = shadow_[sidx], new1;
151 do
152 {
153 new1 = (old1 & ~smask) | sval;
154 } while (!__atomic_compare_exchange_n(shadow_ + sidx, &old1, new1,
155 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
156 }
157 // Sets dirty bits.
158 unsigned d = offset / granularity_;
160 unsigned de = (offset + size - 1)/granularity_;
162 if (de > highestDirty_) highestDirty_ = de;
163 while (d <= de) {
164 __atomic_fetch_or(dirty_ + (d >> 5),(UINT32_C(1)<<(d & 31)), __ATOMIC_SEQ_CST);
165 d++;
166 }
167 return *this;
168 }
169
170 unsigned get_multi(unsigned offset, unsigned size) override {
171 HASSERT(size <= 32);
172 HASSERT(size > 0);
173 HASSERT(offset + size <= size_);
174 unsigned val = shadow_[offset >> 5] >> (offset & 31);
175 int rollover = size + (offset & 31) - 32;
176 if (rollover > 0) {
177 val |= shadow_[(offset >> 5) + 1] << (size - rollover);
178 }
179 if (size < 32) {
180 val &= (UINT32_C(1) << size) - 1;
181 }
182 return val;
183 }
184
185 unsigned size() override {
186 return size_;
187 }
188
189 void lock_and_flush() override {
190 AtomicHolder h(this);
191 flush();
192 }
193
194protected:
198 ShadowedStoredBitSet(unsigned size, uint8_t granularity)
199 : size_(size), granularity_(granularity) {
200 unsigned ssize = (size + 31) >> 5;
201 shadow_ = new uint32_t[ssize];
202 memset(shadow_, 0, ssize * 4);
203 unsigned dsize = (num_cells() + 31) >> 5;
204 dirty_ = new uint32_t[dsize];
205 memset(dirty_, 0, dsize * 4);
206 HASSERT(num_cells() < NO_CELL);
207 }
208
210 delete[] shadow_;
211 delete[] dirty_;
212 }
213
215 typedef uint8_t cell_offs_t;
217 typedef unsigned bit_offs_t;
218
219 static constexpr cell_offs_t NO_CELL = static_cast<cell_offs_t>(-1);
220
224 if (lowestDirty_ > highestDirty_ || lowestDirty_ >= num_cells()) {
225 lowestDirty_ = NO_CELL;
226 highestDirty_ = 0;
227 return NO_CELL;
228 }
229 unsigned dirty_idx = lowestDirty_ / 32;
230 bool fresh_dirty = false;
231 while (dirty_idx < dirty_size_uint32() && dirty_[dirty_idx] == 0) {
232 ++dirty_idx;
233 fresh_dirty = true;
234 }
235 if (dirty_idx >= dirty_size_uint32()) {
236 lowestDirty_ = NO_CELL;
237 highestDirty_ = 0;
238 return NO_CELL;
239 }
240 unsigned bit_ofs = 0;
241 if (!fresh_dirty) {
242 bit_ofs = lowestDirty_ & 31;
243 } else {
244 }
245 while (((UINT32_C(1)<<bit_ofs) & dirty_[dirty_idx]) == 0) {
246 ++bit_ofs;
247 }
248 lowestDirty_ = (dirty_idx << 5) + bit_ofs;
249 return lowestDirty_;
250 }
251
255 HASSERT(cell < num_cells());
256 dirty_[cell >> 5] &= ~(UINT32_C(1)<<(cell & 31));
257 if (cell == lowestDirty_) {
258 ++lowestDirty_;
259 }
260 }
261
265 HASSERT(cell < num_cells());
266 return dirty_[cell >>5] & (UINT32_C(1)<<(cell & 31));
267 }
268
271 return (size_ + granularity_ - 1) / granularity_;
272 }
273
274private:
276 unsigned dirty_size_uint32() {
277 return (num_cells() + 31) / 32;
278 }
279
283 {
284 cell_offs_t old_val = lowestDirty_;
285 do
286 {
287 if (d >= old_val)
288 break;
289 } while (!__atomic_compare_exchange_n(&lowestDirty_, &old_val, d, false,
290 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
291 old_val = highestDirty_;
292 do
293 {
294 if (d <= old_val)
295 break;
296 } while (!__atomic_compare_exchange_n(&highestDirty_, &old_val, d,
297 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST));
298 }
299
301 const unsigned size_;
303 const uint8_t granularity_;
306 cell_offs_t highestDirty_{0};
307
309 uint32_t *shadow_;
313 uint32_t *dirty_;
314};
315
316#endif // _UTILS_STOREDBITSET_HXX_
See OSMutexLock in os/OS.hxx.
Definition Atomic.hxx:153
Lightweight locking class for protecting small critical sections.
Definition Atomic.hxx:130
cell_offs_t lowestDirty_
Helper values for iterating over the dirty bits.
ShadowedStoredBitSet(unsigned size, uint8_t granularity)
uint32_t * shadow_
Data of the actual bits. length is size_ / 32 rounded up.
StoredBitSet & set_multi(unsigned offset, unsigned size, unsigned value) override
Sets a block of consecutive bits.
bool is_dirty(cell_offs_t cell)
StoredBitSet & set_bit(unsigned offset, bool value) override
Sets an individual bit to a specific value.
cell_offs_t next_dirty()
uint32_t * dirty_
Bit set telling whether the individual cells are dirty or not (i.e.
unsigned bit_offs_t
Type indexing all bits.
unsigned size() override
void clear_dirty(cell_offs_t cell)
Clears the dirty bit for a given cell.
const unsigned size_
Total number of bits.
const uint8_t granularity_
How many bits per cell.
unsigned get_multi(unsigned offset, unsigned size) override
Returns a block of consecutive bits as an integer value.
void update_dirty_bounds(cell_offs_t d)
updates lowestDirty_ and highestDirty_ for a given cell.
void lock_and_flush() override
Grabs a lock and writes the current values to persistent storage.
bool get_bit(unsigned offset) override
Retrieves an individual bit.
cell_offs_t num_cells()
uint8_t cell_offs_t
Type indexing cells. Must be an unsigned type.
Abstract class for representing a set of numbered bits that are stored persistently in some backing s...
virtual void flush()=0
Writes the current values to persistent storage.
virtual unsigned get_multi(unsigned offset, unsigned size)=0
Returns a block of consecutive bits as an integer value.
virtual bool get_bit(unsigned offset)=0
Retrieves an individual bit.
virtual StoredBitSet & set_multi(unsigned offset, unsigned size, unsigned value)=0
Sets a block of consecutive bits.
virtual void lock_and_flush()=0
Grabs a lock and writes the current values to persistent storage.
virtual unsigned size()=0
virtual StoredBitSet & set_bit(unsigned offset, bool value)=0
Sets an individual bit to a specific value.
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138