Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
dbitset.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstdint>
5#include <type_traits>
6
7#include "gaia/cnt/bitset_iterator.h"
8#include "gaia/core/utility.h"
9#include "gaia/mem/mem_alloc.h"
10#include "gaia/mem/mem_utils.h"
11
12namespace gaia {
13 namespace cnt {
16 template <typename Allocator = mem::DefaultAllocatorAdaptor>
17 class dbitset {
18 private:
19 struct size_type_selector {
20 static constexpr bool Use32Bit = sizeof(size_t) == 4;
21 using type = std::conditional_t<Use32Bit, uint32_t, uint64_t>;
22 };
23
24 using difference_type = typename size_type_selector::type;
25 using size_type = typename size_type_selector::type;
26 using value_type = size_type;
27 using reference = size_type&;
28 using const_reference = const size_type&;
29 using pointer = size_type*;
30 using const_pointer = const size_type*;
31
32 static constexpr uint32_t BitsPerItem = sizeof(typename size_type_selector::type) * 8;
33
34 pointer m_pData = nullptr;
35 uint32_t m_cnt = uint32_t(0);
36 uint32_t m_cap = uint32_t(0);
37
38 uint32_t items() const {
39 return (m_cnt + BitsPerItem - 1) / BitsPerItem;
40 }
41
42 bool has_trailing_bits() const {
43 return (m_cnt % BitsPerItem) != 0;
44 }
45
46 size_type last_item_mask() const {
47 return ((size_type)1 << (m_cnt % BitsPerItem)) - 1;
48 }
49
50 void try_grow(uint32_t bitsWanted) {
51 uint32_t itemsOld = items();
52 if GAIA_UNLIKELY (bitsWanted > size())
53 m_cnt = bitsWanted;
54 if GAIA_LIKELY (m_cnt <= capacity())
55 return;
56
57 // Increase the size of an existing array.
58 // We are pessimistic with our allocations and only allocate as much as we need.
59 // If we know the expected size ahead of the time a manual call to reserve is necessary.
60 const uint32_t itemsNew = (m_cnt + BitsPerItem - 1) / BitsPerItem;
61 m_cap = itemsNew * BitsPerItem;
62
63 pointer pDataOld = m_pData;
64 m_pData = mem::AllocHelper::alloc<size_type, Allocator>(itemsNew);
65
66 if (pDataOld == nullptr) {
67 // Make sure the new data is set to zeros
68 GAIA_FOR(itemsNew) m_pData[i] = 0;
69 } else {
70 // Copy the old data over and set the old data to zeros
71 mem::copy_elements<size_type, false>((uint8_t*)m_pData, (const uint8_t*)pDataOld, itemsOld, 0, 0, 0);
72 GAIA_FOR2(itemsOld, itemsNew) m_pData[i] = 0;
73
74 // Release the old data
75 mem::AllocHelper::free<Allocator>((void*)pDataOld);
76 }
77 }
78
80 size_type data(uint32_t wordIdx) const {
81 return m_pData[wordIdx];
82 }
83
84 public:
93
95 friend iter;
96 friend iter_inv;
97 friend iter_rev;
98 friend iter_rev_inv;
100
102 dbitset(): m_cnt(1) {
103 // Allocate at least 128 bits
104 reserve(128);
105 }
106
112
113 ~dbitset() {
114 mem::AllocHelper::free<Allocator>((void*)m_pData);
115 }
116
119 dbitset(const dbitset& other) {
120 resize(other.m_cnt);
121 mem::copy_elements<size_type, false>((uint8_t*)m_pData, (const uint8_t*)other.m_pData, other.items(), 0, 0, 0);
122 }
123
127 dbitset& operator=(const dbitset& other) {
128 GAIA_ASSERT(core::addressof(other) != this);
129
130 resize(other.m_cnt);
131 mem::copy_elements<size_type, false>((uint8_t*)m_pData, (const uint8_t*)other.m_pData, other.items(), 0, 0, 0);
132 return *this;
133 }
134
137 dbitset(dbitset&& other) noexcept {
138 m_pData = other.m_pData;
139 m_cnt = other.m_cnt;
140 m_cap = other.m_cap;
141
142 other.m_pData = nullptr;
143 other.m_cnt = 0;
144 other.m_cap = 0;
145 }
146
150 dbitset& operator=(dbitset&& other) noexcept {
151 GAIA_ASSERT(core::addressof(other) != this);
152
153 m_pData = other.m_pData;
154 m_cnt = other.m_cnt;
155 m_cap = other.m_cap;
156
157 other.m_pData = nullptr;
158 other.m_cnt = 0;
159 other.m_cap = 0;
160 return *this;
161 }
162
166 // Make sure at least one bit is requested
167 if (bitsWanted < 1)
168 bitsWanted = 1;
169
170 // Nothing to do if the capacity is already bigger than requested
171 if (bitsWanted <= capacity())
172 return;
173
174 const uint32_t itemsOld = m_cap / BitsPerItem;
175 const uint32_t itemsNew = (bitsWanted + BitsPerItem - 1) / BitsPerItem;
176 if (itemsOld != itemsNew) {
177 auto* pDataOld = m_pData;
178 m_pData = mem::AllocHelper::alloc<size_type, Allocator>(itemsNew);
179
180 if (pDataOld == nullptr) {
181 // Make sure the new data is set to zeros
182 GAIA_FOR(itemsNew) m_pData[i] = 0;
183 } else {
184 const uint32_t itemsOld2 = items();
185 // Copy the old data over and set the old data to zeros
186 mem::copy_elements<size_type, false>((uint8_t*)m_pData, (const uint8_t*)pDataOld, itemsOld2, 0, 0, 0);
187 GAIA_FOR2(itemsOld2, itemsNew) m_pData[i] = 0;
188
189 // Release old data
190 mem::AllocHelper::free<Allocator>(pDataOld);
191 }
192 }
193
194 m_cap = itemsNew * BitsPerItem;
195 }
196
200 // Make sure at least one bit is requested
201 if (bitsWanted < 1)
202 bitsWanted = 1;
203
204 // Nothing to do if the capacity is already bigger than requested
205 if (bitsWanted == size())
206 return;
207
208 const uint32_t itemsOld = m_cap / BitsPerItem;
209 const uint32_t itemsNew = (bitsWanted + BitsPerItem - 1) / BitsPerItem;
210 if (itemsOld != itemsNew) {
211 auto* pDataOld = m_pData;
212 m_pData = mem::AllocHelper::alloc<size_type, Allocator>(itemsNew);
213
214 if (pDataOld == nullptr) {
215 // Make sure the new data is set to zeros
216 GAIA_FOR(itemsNew) m_pData[i] = 0;
217 } else {
218 // Copy only the storage that exists in both allocations. resize() can shrink the
219 // backing store when the requested bit count is smaller than the current size.
221 mem::copy_elements<size_type, false>((uint8_t*)m_pData, (const uint8_t*)pDataOld, itemsToCopy, 0, 0, 0);
222 // Set the old data to zeros.
223 // If resizing to a smaller size this will do nothing
224 GAIA_FOR2(itemsToCopy, itemsNew) m_pData[i] = 0;
225
226 // Release old data
227 mem::AllocHelper::free<Allocator>((void*)pDataOld);
228 }
229
230 m_cap = itemsNew * BitsPerItem;
231 }
232
233 m_cnt = bitsWanted;
234 }
235
238 iter begin() const {
239 return iter(*this, 0, true);
240 }
241
244 iter end() const {
245 return iter(*this, size(), false);
246 }
247
250 iter_rev rbegin() const {
251 return iter_rev(*this, size(), false);
252 }
253
256 iter_rev rend() const {
257 return iter_rev(*this, 0, true);
258 }
259
262 iter_inv ibegin() const {
263 return iter_inv(*this, 0, true);
264 }
265
268 iter_inv iend() const {
269 return iter_inv(*this, size(), false);
270 }
271
275 return iter_rev_inv(*this, size(), false);
276 }
277
281 return iter_rev_inv(*this, 0, true);
282 }
283
287 GAIA_NODISCARD bool operator[](uint32_t pos) const {
288 return test(pos);
289 }
290
294 GAIA_NODISCARD bool operator==(const dbitset& other) const {
295 const uint32_t item_count = items();
296 GAIA_FOR(item_count) {
297 if (m_pData[i] != other.m_pData[i])
298 return false;
299 }
300 return true;
301 }
302
306 GAIA_NODISCARD bool operator!=(const dbitset& other) const {
307 const uint32_t item_count = items();
308 GAIA_FOR(item_count) {
309 if (m_pData[i] == other.m_pData[i])
310 return false;
311 }
312 return true;
313 }
314
316 void set() {
317 if GAIA_UNLIKELY (size() == 0)
318 return;
319
320 const auto item_count = items();
321 const auto lastItemMask = last_item_mask();
322
323 if (lastItemMask != 0) {
324 GAIA_FOR(item_count - 1) m_pData[i] = (size_type)-1;
325 m_pData[item_count - 1] = lastItemMask;
326 } else {
327 GAIA_FOR(item_count) m_pData[i] = (size_type)-1;
328 }
329 }
330
334 void set(uint32_t pos, bool value = true) {
335 try_grow(pos + 1);
336
337 if (value)
338 m_pData[pos / BitsPerItem] |= ((size_type)1 << (pos % BitsPerItem));
339 else
340 m_pData[pos / BitsPerItem] &= ~((size_type)1 << (pos % BitsPerItem));
341 }
342
344 void flip() {
345 if GAIA_UNLIKELY (size() == 0)
346 return;
347
348 const auto item_count = items();
349 const auto lastItemMask = last_item_mask();
350
351 if (lastItemMask != 0) {
352 GAIA_FOR(item_count - 1) m_pData[i] = ~m_pData[i];
353 m_pData[item_count - 1] = (~m_pData[item_count - 1]) & lastItemMask;
354 } else {
355 GAIA_FOR(item_count + 1) m_pData[i] = ~m_pData[i];
356 }
357 }
358
362 GAIA_ASSERT(pos < size());
363 m_pData[pos / BitsPerItem] ^= ((size_type)1 << (pos % BitsPerItem));
364 }
365
371 GAIA_ASSERT(bitFrom <= bitTo);
372 GAIA_ASSERT(bitTo < size());
373
374 if GAIA_UNLIKELY (size() == 0)
375 return *this;
376
377 const uint32_t wordIdxFrom = bitFrom / BitsPerItem;
378 const uint32_t wordIdxTo = bitTo / BitsPerItem;
379
380 auto getMask = [](uint32_t from, uint32_t to) -> size_type {
381 const auto diff = to - from;
382 // Set all bits when asking for the full range
383 if (diff == BitsPerItem - 1)
384 return (size_type)-1;
385
386 return ((size_type(1) << (diff + 1)) - 1) << from;
387 };
388
389 if (wordIdxFrom == wordIdxTo) {
390 m_pData[wordIdxTo] ^= getMask(bitFrom % BitsPerItem, bitTo % BitsPerItem);
391 } else {
392 // First word
393 m_pData[wordIdxFrom] ^= getMask(bitFrom % BitsPerItem, BitsPerItem - 1);
394 // Middle
395 GAIA_FOR2(wordIdxFrom + 1, wordIdxTo) m_pData[i] = ~m_pData[i];
396 // Last word
397 m_pData[wordIdxTo] ^= getMask(0, bitTo % BitsPerItem);
398 }
399
400 return *this;
401 }
402
404 void reset() {
405 const auto item_count = items();
406 GAIA_FOR(item_count) m_pData[i] = 0;
407 }
408
412 GAIA_ASSERT(pos < size());
413 m_pData[pos / BitsPerItem] &= ~((size_type)1 << (pos % BitsPerItem));
414 }
415
419 GAIA_NODISCARD bool test(uint32_t pos) const {
420 GAIA_ASSERT(pos < size());
421 return (m_pData[pos / BitsPerItem] & ((size_type)1 << (pos % BitsPerItem))) != 0;
422 }
423
426 GAIA_NODISCARD bool all() const {
427 const auto item_count = items() - 1;
428 const auto lastItemMask = last_item_mask();
429
430 GAIA_FOR(item_count) {
431 if (m_pData[i] != (size_type)-1)
432 return false;
433 }
434
435 if (has_trailing_bits())
436 return (m_pData[item_count] & lastItemMask) == lastItemMask;
437
438 return m_pData[item_count] == (size_type)-1;
439 }
440
443 GAIA_NODISCARD bool any() const {
444 const auto item_count = items();
445 GAIA_FOR(item_count) {
446 if (m_pData[i] != 0)
447 return true;
448 }
449 return false;
450 }
451
454 GAIA_NODISCARD bool none() const {
455 const auto item_count = items();
456 GAIA_FOR(item_count) {
457 if (m_pData[i] != 0)
458 return false;
459 }
460 return true;
461 }
462
465 GAIA_NODISCARD uint32_t count() const {
466 uint32_t total = 0;
467
468 const auto item_count = items();
469
470 GAIA_MSVC_WARNING_PUSH()
471 GAIA_MSVC_WARNING_DISABLE(4244)
472 if constexpr (sizeof(size_type) == 4) {
473 GAIA_FOR(item_count) total += GAIA_POPCNT(m_pData[i]);
474 } else {
475 GAIA_FOR(item_count) total += GAIA_POPCNT64(m_pData[i]);
476 }
477 GAIA_MSVC_WARNING_POP()
478
479 return total;
480 }
481
484 GAIA_NODISCARD constexpr uint32_t size() const {
485 return m_cnt;
486 }
487
490 GAIA_NODISCARD constexpr uint32_t capacity() const {
491 return m_cap;
492 }
493 };
494 } // namespace cnt
495} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:27
Dynamically sized bit set.
Definition dbitset.h:17
GAIA_NODISCARD bool none() const
Checks if all bits are reset.
Definition dbitset.h:454
dbitset(uint32_t reserveBits)
Constructs a bit set with requested initial capacity and a size of one bit.
Definition dbitset.h:109
void reset()
Unsets all bits.
Definition dbitset.h:404
dbitset()
Constructs a bit set with capacity for at least 128 bits and an initial size of one bit.
Definition dbitset.h:102
const_iterator< dbitset > iter
Forward iterator over set bit indices.
Definition dbitset.h:86
void set(uint32_t pos, bool value=true)
Sets one bit, growing the bit set when needed.
Definition dbitset.h:334
const_iterator_inverse< dbitset > iter_inv
Forward iterator over unset bit indices.
Definition dbitset.h:88
GAIA_NODISCARD uint32_t count() const
Returns the number of set bits.
Definition dbitset.h:465
GAIA_NODISCARD constexpr uint32_t capacity() const
Returns the number of bits the dbitset can hold.
Definition dbitset.h:490
const_reverse_inverse_iterator< dbitset > iter_rev_inv
Reverse iterator over unset bit indices.
Definition dbitset.h:92
iter_inv ibegin() const
Returns an iterator to the first unset bit.
Definition dbitset.h:262
iter_rev_inv ribegin() const
Returns an iterator to the last unset bit.
Definition dbitset.h:274
GAIA_NODISCARD bool operator==(const dbitset &other) const
Compares two bit sets for equality.
Definition dbitset.h:294
dbitset(dbitset &&other) noexcept
Move-constructs a bit set and leaves the source empty.
Definition dbitset.h:137
iter_rev rend() const
Returns the reverse set-bit sentinel.
Definition dbitset.h:256
GAIA_NODISCARD constexpr uint32_t size() const
Returns the number of bits the dbitset holds.
Definition dbitset.h:484
iter_rev rbegin() const
Returns an iterator to the last set bit.
Definition dbitset.h:250
dbitset & operator=(const dbitset &other)
Copy-assigns a bit set.
Definition dbitset.h:127
iter end() const
Returns the forward set-bit sentinel.
Definition dbitset.h:244
GAIA_NODISCARD bool operator[](uint32_t pos) const
Tests a bit.
Definition dbitset.h:287
GAIA_NODISCARD bool all() const
Checks if all bits are set.
Definition dbitset.h:426
void flip(uint32_t pos)
Flips one bit.
Definition dbitset.h:361
GAIA_NODISCARD bool test(uint32_t pos) const
Returns the value of one bit.
Definition dbitset.h:419
iter_rev_inv riend() const
Returns the reverse unset-bit sentinel.
Definition dbitset.h:280
GAIA_NODISCARD bool operator!=(const dbitset &other) const
Compares two bit sets for inequality.
Definition dbitset.h:306
void flip()
Flips all bits.
Definition dbitset.h:344
dbitset(const dbitset &other)
Copy-constructs a bit set.
Definition dbitset.h:119
void reserve(uint32_t bitsWanted)
Reserves storage without changing the current bit count.
Definition dbitset.h:165
void resize(uint32_t bitsWanted)
Changes the number of addressable bits.
Definition dbitset.h:199
const_reverse_iterator< dbitset > iter_rev
Reverse iterator over set bit indices.
Definition dbitset.h:90
iter_inv iend() const
Returns the forward unset-bit sentinel.
Definition dbitset.h:268
dbitset & operator=(dbitset &&other) noexcept
Move-assigns a bit set and leaves the source empty.
Definition dbitset.h:150
void set()
Sets all bits.
Definition dbitset.h:316
GAIA_NODISCARD bool any() const
Checks if any bit is set.
Definition dbitset.h:443
iter begin() const
Returns an iterator to the first set bit.
Definition dbitset.h:238
void reset(uint32_t pos)
Unsets one bit.
Definition dbitset.h:411
dbitset & flip(uint32_t bitFrom, uint32_t bitTo)
Flips an inclusive range of bits.
Definition dbitset.h:370