Gaia-ECS v0.9.3
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 {
14 template <typename Allocator = mem::DefaultAllocatorAdaptor>
15 class dbitset {
16 private:
17 struct size_type_selector {
18 static constexpr bool Use32Bit = sizeof(size_t) == 4;
19 using type = std::conditional_t<Use32Bit, uint32_t, uint64_t>;
20 };
21
22 using difference_type = typename size_type_selector::type;
23 using size_type = typename size_type_selector::type;
24 using value_type = size_type;
25 using reference = size_type&;
26 using const_reference = const size_type&;
27 using pointer = size_type*;
28 using const_pointer = const size_type*;
29
30 static constexpr uint32_t BitsPerItem = sizeof(typename size_type_selector::type) * 8;
31
32 pointer m_pData = nullptr;
33 uint32_t m_cnt = uint32_t(0);
34 uint32_t m_cap = uint32_t(0);
35
36 uint32_t items() const {
37 return (m_cnt + BitsPerItem - 1) / BitsPerItem;
38 }
39
40 bool has_trailing_bits() const {
41 return (m_cnt % BitsPerItem) != 0;
42 }
43
44 size_type last_item_mask() const {
45 return ((size_type)1 << (m_cnt % BitsPerItem)) - 1;
46 }
47
48 void try_grow(uint32_t bitsWanted) {
49 uint32_t itemsOld = items();
50 if GAIA_UNLIKELY (bitsWanted > size())
51 m_cnt = bitsWanted;
52 if GAIA_LIKELY (m_cnt <= capacity())
53 return;
54
55 // Increase the size of an existing array.
56 // We are pessimistic with our allocations and only allocate as much as we need.
57 // If we know the expected size ahead of the time a manual call to reserve is necessary.
58 const uint32_t itemsNew = (m_cnt + BitsPerItem - 1) / BitsPerItem;
59 m_cap = itemsNew * BitsPerItem;
60
61 pointer pDataOld = m_pData;
62 m_pData = mem::AllocHelper::alloc<size_type, Allocator>(itemsNew);
63
64 if (pDataOld == nullptr) {
65 // Make sure the new data is set to zeros
66 GAIA_FOR(itemsNew) m_pData[i] = 0;
67 } else {
68 // Copy the old data over and set the old data to zeros
69 mem::copy_elements<size_type, false>((uint8_t*)m_pData, (const uint8_t*)pDataOld, itemsOld, 0, 0, 0);
70 GAIA_FOR2(itemsOld, itemsNew) m_pData[i] = 0;
71
72 // Release the old data
73 mem::AllocHelper::free<Allocator>((void*)pDataOld);
74 }
75 }
76
78 size_type data(uint32_t wordIdx) const {
79 return m_pData[wordIdx];
80 }
81
82 public:
87
88 friend iter;
89 friend iter_inv;
90 friend iter_rev;
91 friend iter_rev_inv;
92
93 dbitset(): m_cnt(1) {
94 // Allocate at least 128 bits
95 reserve(128);
96 }
97
98 dbitset(uint32_t reserveBits): m_cnt(1) {
99 reserve(reserveBits);
100 }
101
102 ~dbitset() {
103 mem::AllocHelper::free<Allocator>((void*)m_pData);
104 }
105
106 dbitset(const dbitset& other) {
107 resize(other.m_cnt);
108 mem::copy_elements<size_type, false>((uint8_t*)m_pData, (const uint8_t*)other.m_pData, other.items(), 0, 0, 0);
109 }
110
111 dbitset& operator=(const dbitset& other) {
112 GAIA_ASSERT(core::addressof(other) != this);
113
114 resize(other.m_cnt);
115 mem::copy_elements<size_type, false>((uint8_t*)m_pData, (const uint8_t*)other.m_pData, other.items(), 0, 0, 0);
116 return *this;
117 }
118
119 dbitset(dbitset&& other) noexcept {
120 m_pData = other.m_pData;
121 m_cnt = other.m_cnt;
122 m_cap = other.m_cap;
123
124 other.m_pData = nullptr;
125 other.m_cnt = 0;
126 other.m_cap = 0;
127 }
128
129 dbitset& operator=(dbitset&& other) noexcept {
130 GAIA_ASSERT(core::addressof(other) != this);
131
132 m_pData = other.m_pData;
133 m_cnt = other.m_cnt;
134 m_cap = other.m_cap;
135
136 other.m_pData = nullptr;
137 other.m_cnt = 0;
138 other.m_cap = 0;
139 return *this;
140 }
141
142 void reserve(uint32_t bitsWanted) {
143 // Make sure at least one bit is requested
144 if (bitsWanted < 1)
145 bitsWanted = 1;
146
147 // Nothing to do if the capacity is already bigger than requested
148 if (bitsWanted <= capacity())
149 return;
150
151 const uint32_t itemsOld = m_cap / BitsPerItem;
152 const uint32_t itemsNew = (bitsWanted + BitsPerItem - 1) / BitsPerItem;
153 if (itemsOld != itemsNew) {
154 auto* pDataOld = m_pData;
155 m_pData = mem::AllocHelper::alloc<size_type, Allocator>(itemsNew);
156
157 if (pDataOld == nullptr) {
158 // Make sure the new data is set to zeros
159 GAIA_FOR(itemsNew) m_pData[i] = 0;
160 } else {
161 const uint32_t itemsOld2 = items();
162 // Copy the old data over and set the old data to zeros
163 mem::copy_elements<size_type, false>((uint8_t*)m_pData, (const uint8_t*)pDataOld, itemsOld2, 0, 0, 0);
164 GAIA_FOR2(itemsOld2, itemsNew) m_pData[i] = 0;
165
166 // Release old data
167 mem::AllocHelper::free<Allocator>(pDataOld);
168 }
169 }
170
171 m_cap = itemsNew * BitsPerItem;
172 }
173
174 void resize(uint32_t bitsWanted) {
175 // Make sure at least one bit is requested
176 if (bitsWanted < 1)
177 bitsWanted = 1;
178
179 // Nothing to do if the capacity is already bigger than requested
180 if (bitsWanted == size())
181 return;
182
183 const uint32_t itemsOld = m_cap / BitsPerItem;
184 const uint32_t itemsNew = (bitsWanted + BitsPerItem - 1) / BitsPerItem;
185 if (itemsOld != itemsNew) {
186 auto* pDataOld = m_pData;
187 m_pData = mem::AllocHelper::alloc<size_type, Allocator>(itemsNew);
188
189 if (pDataOld == nullptr) {
190 // Make sure the new data is set to zeros
191 GAIA_FOR(itemsNew) m_pData[i] = 0;
192 } else {
193 const uint32_t itemsOld2 = items();
194 // Copy the old data over
195 mem::copy_elements<size_type, false>((uint8_t*)m_pData, (const uint8_t*)pDataOld, itemsOld2, 0, 0, 0);
196 // Set the old data to zeros.
197 // If resizing to a smaller size this will do nothing
198 GAIA_FOR2(itemsOld2, itemsNew) m_pData[i] = 0;
199
200 // Release old data
201 mem::AllocHelper::free<Allocator>((void*)pDataOld);
202 }
203
204 m_cap = itemsNew * BitsPerItem;
205 }
206
207 m_cnt = bitsWanted;
208 }
209
210 iter begin() const {
211 return iter(*this, 0, true);
212 }
213
214 iter end() const {
215 return iter(*this, size(), false);
216 }
217
218 iter_rev rbegin() const {
219 return iter_rev(*this, size(), false);
220 }
221
222 iter_rev rend() const {
223 return iter_rev(*this, 0, true);
224 }
225
226 iter_inv ibegin() const {
227 return iter_inv(*this, 0, true);
228 }
229
230 iter_inv iend() const {
231 return iter_inv(*this, size(), false);
232 }
233
234 iter_rev_inv ribegin() const {
235 return iter_rev_inv(*this, size(), false);
236 }
237
238 iter_rev_inv riend() const {
239 return iter_rev_inv(*this, 0, true);
240 }
241
242 GAIA_NODISCARD bool operator[](uint32_t pos) const {
243 return test(pos);
244 }
245
246 GAIA_NODISCARD bool operator==(const dbitset& other) const {
247 const uint32_t item_count = items();
248 GAIA_FOR(item_count) {
249 if (m_pData[i] != other.m_pData[i])
250 return false;
251 }
252 return true;
253 }
254
255 GAIA_NODISCARD bool operator!=(const dbitset& other) const {
256 const uint32_t item_count = items();
257 GAIA_FOR(item_count) {
258 if (m_pData[i] == other.m_pData[i])
259 return false;
260 }
261 return true;
262 }
263
265 void set() {
266 if GAIA_UNLIKELY (size() == 0)
267 return;
268
269 const auto item_count = items();
270 const auto lastItemMask = last_item_mask();
271
272 if (lastItemMask != 0) {
273 GAIA_FOR(item_count - 1) m_pData[i] = (size_type)-1;
274 m_pData[item_count - 1] = lastItemMask;
275 } else {
276 GAIA_FOR(item_count) m_pData[i] = (size_type)-1;
277 }
278 }
279
281 void set(uint32_t pos, bool value = true) {
282 try_grow(pos + 1);
283
284 if (value)
285 m_pData[pos / BitsPerItem] |= ((size_type)1 << (pos % BitsPerItem));
286 else
287 m_pData[pos / BitsPerItem] &= ~((size_type)1 << (pos % BitsPerItem));
288 }
289
291 void flip() {
292 if GAIA_UNLIKELY (size() == 0)
293 return;
294
295 const auto item_count = items();
296 const auto lastItemMask = last_item_mask();
297
298 if (lastItemMask != 0) {
299 GAIA_FOR(item_count - 1) m_pData[i] = ~m_pData[i];
300 m_pData[item_count - 1] = (~m_pData[item_count - 1]) & lastItemMask;
301 } else {
302 GAIA_FOR(item_count + 1) m_pData[i] = ~m_pData[i];
303 }
304 }
305
307 void flip(uint32_t pos) {
308 GAIA_ASSERT(pos < size());
309 m_pData[pos / BitsPerItem] ^= ((size_type)1 << (pos % BitsPerItem));
310 }
311
313 dbitset& flip(uint32_t bitFrom, uint32_t bitTo) {
314 GAIA_ASSERT(bitFrom <= bitTo);
315 GAIA_ASSERT(bitTo < size());
316
317 if GAIA_UNLIKELY (size() == 0)
318 return *this;
319
320 const uint32_t wordIdxFrom = bitFrom / BitsPerItem;
321 const uint32_t wordIdxTo = bitTo / BitsPerItem;
322
323 auto getMask = [](uint32_t from, uint32_t to) -> size_type {
324 const auto diff = to - from;
325 // Set all bits when asking for the full range
326 if (diff == BitsPerItem - 1)
327 return (size_type)-1;
328
329 return ((size_type(1) << (diff + 1)) - 1) << from;
330 };
331
332 if (wordIdxFrom == wordIdxTo) {
333 m_pData[wordIdxTo] ^= getMask(bitFrom % BitsPerItem, bitTo % BitsPerItem);
334 } else {
335 // First word
336 m_pData[wordIdxFrom] ^= getMask(bitFrom % BitsPerItem, BitsPerItem - 1);
337 // Middle
338 GAIA_FOR2(wordIdxFrom + 1, wordIdxTo) m_pData[i] = ~m_pData[i];
339 // Last word
340 m_pData[wordIdxTo] ^= getMask(0, bitTo % BitsPerItem);
341 }
342
343 return *this;
344 }
345
347 void reset() {
348 const auto item_count = items();
349 GAIA_FOR(item_count) m_pData[i] = 0;
350 }
351
353 void reset(uint32_t pos) {
354 GAIA_ASSERT(pos < size());
355 m_pData[pos / BitsPerItem] &= ~((size_type)1 << (pos % BitsPerItem));
356 }
357
359 GAIA_NODISCARD bool test(uint32_t pos) const {
360 GAIA_ASSERT(pos < size());
361 return (m_pData[pos / BitsPerItem] & ((size_type)1 << (pos % BitsPerItem))) != 0;
362 }
363
365 GAIA_NODISCARD bool all() const {
366 const auto item_count = items() - 1;
367 const auto lastItemMask = last_item_mask();
368
369 GAIA_FOR(item_count) {
370 if (m_pData[i] != (size_type)-1)
371 return false;
372 }
373
374 if (has_trailing_bits())
375 return (m_pData[item_count] & lastItemMask) == lastItemMask;
376
377 return m_pData[item_count] == (size_type)-1;
378 }
379
381 GAIA_NODISCARD bool any() const {
382 const auto item_count = items();
383 GAIA_FOR(item_count) {
384 if (m_pData[i] != 0)
385 return true;
386 }
387 return false;
388 }
389
391 GAIA_NODISCARD bool none() const {
392 const auto item_count = items();
393 GAIA_FOR(item_count) {
394 if (m_pData[i] != 0)
395 return false;
396 }
397 return true;
398 }
399
401 GAIA_NODISCARD uint32_t count() const {
402 uint32_t total = 0;
403
404 const auto item_count = items();
405
406 GAIA_MSVC_WARNING_PUSH()
407 GAIA_MSVC_WARNING_DISABLE(4244)
408 if constexpr (sizeof(size_type) == 4) {
409 GAIA_FOR(item_count) total += GAIA_POPCNT(m_pData[i]);
410 } else {
411 GAIA_FOR(item_count) total += GAIA_POPCNT64(m_pData[i]);
412 }
413 GAIA_MSVC_WARNING_POP()
414
415 return total;
416 }
417
419 GAIA_NODISCARD constexpr uint32_t size() const {
420 return m_cnt;
421 }
422
424 GAIA_NODISCARD constexpr uint32_t capacity() const {
425 return m_cap;
426 }
427 };
428 } // namespace cnt
429} // namespace gaia
Bitset iterator.
Definition bitset_iterator.h:14
Definition dbitset.h:15
GAIA_NODISCARD bool none() const
Checks if all bits are reset.
Definition dbitset.h:391
void reset()
Unsets all bits.
Definition dbitset.h:347
void set(uint32_t pos, bool value=true)
Sets the bit at the postion.
Definition dbitset.h:281
GAIA_NODISCARD uint32_t count() const
Returns the number of set bits.
Definition dbitset.h:401
GAIA_NODISCARD constexpr uint32_t capacity() const
Returns the number of bits the dbitset can hold.
Definition dbitset.h:424
GAIA_NODISCARD constexpr uint32_t size() const
Returns the number of bits the dbitset holds.
Definition dbitset.h:419
GAIA_NODISCARD bool all() const
Checks if all bits are set.
Definition dbitset.h:365
void flip(uint32_t pos)
Flips the bit at the postion.
Definition dbitset.h:307
GAIA_NODISCARD bool test(uint32_t pos) const
Returns the value of the bit at the position.
Definition dbitset.h:359
void flip()
Flips all bits.
Definition dbitset.h:291
void set()
Sets all bits.
Definition dbitset.h:265
GAIA_NODISCARD bool any() const
Checks if any bit is set.
Definition dbitset.h:381
void reset(uint32_t pos)
Unsets the bit at the postion.
Definition dbitset.h:353
dbitset & flip(uint32_t bitFrom, uint32_t bitTo)
Flips all bits from.
Definition dbitset.h:313
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9