CommonLibSSE NG
GHashSetBase.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "RE/G/GAllocator.h"
4 #include "RE/G/GFixedSizeHash.h"
6 #include "RE/G/GMath.h"
7 #include "RE/G/GMemory.h"
8 
9 namespace RE
10 {
11  template <
12  class C,
13  class HashF = GFixedSizeHash<C>,
14  class AltHashF = HashF,
15  class Allocator = GAllocatorGH<C>,
16  class Entry = GHashsetCachedEntry<C, HashF>>
18  {
19  public:
21 
23  {
24  public:
26  hash(nullptr),
27  index(0)
28  {}
29 
30  const C& operator*() const
31  {
32  assert(index >= 0 && index <= (SPInt)hash->table->sizeMask);
33  return hash->E(index).value;
34  }
35 
36  const C* operator->() const
37  {
38  assert(index >= 0 && index <= (SPInt)hash->table->sizeMask);
39  return &hash->E(index).value;
40  }
41 
42  void operator++()
43  {
44  if (index <= (SPInt)hash->table->sizeMask) {
45  ++index;
46  while ((UPInt)index <= hash->table->sizeMask && hash->E(index).IsEmpty()) {
47  ++index;
48  }
49  }
50  }
51 
52  bool operator==(const const_iterator& a_it) const
53  {
54  if (IsEnd() && a_it.IsEnd()) {
55  return true;
56  } else {
57  return (hash == a_it.hash) && (index == a_it.index);
58  }
59  }
60 
61  bool operator!=(const const_iterator& a_it) const
62  {
63  return !(*this == a_it);
64  }
65 
66  [[nodiscard]] bool IsEnd() const
67  {
68  return (!hash) || (!hash->table) || (index > (SPInt)hash->table->sizeMask);
69  }
70 
71  protected:
72  friend class GHashSetBase<C, HashF, AltHashF, Allocator, Entry>;
73 
74  const_iterator(const SelfType* a_hash, SPInt a_index) :
75  hash(a_hash),
76  index(a_index)
77  {}
78 
79  const SelfType* hash; // 00
80  SPInt index; // 08
81  };
82  static_assert(sizeof(const_iterator) == 0x10);
83 
84  friend struct const_iterator;
85 
86  struct iterator : public const_iterator
87  {
88  public:
90  const_iterator(nullptr, 0)
91  {}
92 
93  C& operator*() const
94  {
95  assert(const_iterator::index >= 0 && const_iterator::index <= (SPInt)const_iterator::hash->table->sizeMask);
96  return const_cast<SelfType*>(const_iterator::hash)->E(const_iterator::index).value;
97  }
98 
99  C* operator->() const
100  {
101  return &(operator*());
102  }
103 
104  void Remove()
105  {
106  SelfType* theHash = const_cast<SelfType*>(const_iterator::hash);
107  Entry* ee = &theHash->E(const_iterator::index);
108  const C& key = ee->value;
109 
110  UPInt hashValue = AltHashF()(key);
111  SPInt index = hashValue & hash->table->sizeMask;
112 
113  Entry* entry = &hash->E(index);
114 
115  if (entry->IsEmpty() || (entry->GetCachedHash(hash->table->sizeMask) != (UPInt)index)) {
116  return;
117  }
118 
119  SPInt naturalIndex = index;
120  SPInt prevIndex = -1;
121 
122  while ((entry->GetCachedHash(hash->table->sizeMask) != (UPInt)naturalIndex) || !(entry->value == key)) {
123  prevIndex = index;
124  index = entry->nextInChain;
125  if (index == -1) {
126  return;
127  }
128  entry = &hash->E(index);
129  }
130 
131  if (index == (SPInt)const_iterator::index) {
132  if (naturalIndex == index) {
133  if (!entry->IsEndOfChain()) {
134  Entry* nextEntry = &hash->E(entry->nextInChain);
135  entry->Clear();
136  new (entry) Entry(*nextEntry);
137  entry = nextEntry;
139  }
140  } else {
141  hash->E(prevIndex).nextInChain = entry->nextInChain;
142  }
143 
144  entry->Clear();
145  --(hash->table->entryCount);
146  } else {
147  assert(false);
148  }
149  }
150 
151  private:
152  friend class GHashSetBase<C, HashF, AltHashF, Allocator, Entry>;
153 
154  using base = const_iterator;
155  using base::hash;
156  using base::index;
157 
158  iterator(SelfType* a_hash, SPInt a_idx) :
159  const_iterator(a_hash, a_idx)
160  {}
161  };
162 
163  friend struct iterator;
164 
166  table(nullptr)
167  {}
168 
169  GHashSetBase(std::int32_t a_sizeHint) :
170  table(0)
171  {
172  SetCapacity(this, a_sizeHint);
173  }
174 
175  explicit GHashSetBase([[maybe_unused]] void* a_memAddr) :
176  table(0)
177  {}
178 
179  GHashSetBase(void* a_memAddr, std::int32_t a_sizeHint) :
180  table(0)
181  {
182  SetCapacity(a_memAddr, a_sizeHint);
183  }
184 
185  GHashSetBase(const SelfType& a_src) :
186  table(0)
187  {
188  Assign(this, a_src);
189  }
190 
192  {
193  if (table) {
194  // Delete the entries.
195  for (UPInt i = 0, n = table->sizeMask; i <= n; i++) {
196  Entry* entry = std::addressof(E(i));
197  if (!entry->IsEmpty()) {
198  entry->Free();
199  }
200  }
201 
202  Allocator::Free(table);
203  table = nullptr;
204  }
205  }
206 
207  GFC_MEMORY_REDEFINE_NEW(GHashSetBase, Allocator::kStatID);
208 
209  void Assign(void* a_memAddr, const SelfType& a_src)
210  {
211  Clear();
212  if (a_src.IsEmpty() == false) {
213  SetCapacity(a_memAddr, a_src.GetSize());
214  for (const_iterator it = a_src.Begin(); it != a_src.End(); ++it) {
215  Add(a_memAddr, *it);
216  }
217  }
218  }
219 
220  void Clear()
221  {
222  if (table) {
223  for (UPInt i = 0, n = table->sizeMask; i <= n; i++) {
224  Entry* entry = std::addressof(E(i));
225  if (!entry->IsEmpty()) {
226  entry->Clear();
227  }
228  }
229 
230  Allocator::Free(table);
231  table = nullptr;
232  }
233  }
234 
235  [[nodiscard]] bool IsEmpty() const
236  {
237  return !table || table->entryCount == 0;
238  }
239 
240  template <class CRef>
241  void Set(void* a_memAddr, const CRef& a_key)
242  {
243  UPInt hashValue = HashF()(a_key);
244  SPInt index = static_cast<SPInt>(-1);
245 
246  if (table) {
247  index = FindIndexCore(a_key, hashValue & table->sizeMask);
248  }
249 
250  if (index >= 0) {
251  E(index).value = a_key;
252  } else {
253  Add(a_memAddr, a_key, hashValue);
254  }
255  }
256 
257  template <class CRef>
258  inline void Add(void* a_memAddr, const CRef& a_key)
259  {
260  const UPInt hashValue = HashF()(a_key);
261  Add(a_memAddr, a_key, hashValue);
262  }
263 
264  template <class K>
265  void RemoveAlt(const K& a_key)
266  {
267  if (!table) {
268  return;
269  }
270 
271  const UPInt hashValue = AltHashF()(a_key);
272  SPInt index = hashValue & table->sizeMask;
273 
274  Entry* entry = std::addressof(E(index));
275 
276  if (entry->IsEmpty() || (entry->GetCachedHash(table->sizeMask) != (UPInt)index)) {
277  return;
278  }
279 
280  // Save index
281  const SPInt naturalIndex = index;
282  SPInt prevIndex = -1;
283 
284  while ((entry->GetCachedHash(table->sizeMask) != (UPInt)naturalIndex) || !(entry->value == a_key)) {
285  prevIndex = index;
286  index = entry->nextInChain;
287  if (index == -1) {
288  return;
289  }
290  entry = std::addressof(E(index));
291  }
292 
293  if (naturalIndex == index) {
294  if (!entry->IsEndOfChain()) {
295  Entry* nextEntry = std::addressof(E(entry->nextInChain));
296  entry->Clear();
297  new (entry) Entry(*nextEntry);
298  entry = nextEntry;
299  }
300  } else {
301  E(prevIndex).nextInChain = entry->nextInChain;
302  }
303 
304  entry->Clear();
305  --(table->entryCount);
306  }
307 
308  template <class CRef>
309  void Remove(const CRef& a_key)
310  {
311  RemoveAlt(a_key);
312  }
313 
314  template <class K>
315  C* Get(const K& a_key)
316  {
317  const SPInt index = FindIndex(a_key);
318  if (index >= 0) {
319  return std::addressof(E(index).value);
320  } else {
321  return 0;
322  }
323  }
324 
325  template <class K>
326  const C* Get(const K& key) const
327  {
328  const SPInt index = FindIndex(key);
329  if (index >= 0) {
330  return std::addressof(E(index).value);
331  } else {
332  return 0;
333  }
334  }
335 
336  template <class K>
337  C* GetAlt(const K& a_key)
338  {
339  const SPInt index = FindIndexAlt(a_key);
340  if (index >= 0)
341  return std::addressof(E(index).value);
342  return nullptr;
343  }
344 
345  template <class K>
346  const C* GetAlt(const K& a_key) const
347  {
348  const SPInt index = FindIndexAlt(a_key);
349  if (index >= 0) {
350  return std::addressof(E(index).value);
351  } else {
352  return nullptr;
353  }
354  }
355 
356  template <class K>
357  bool GetAlt(const K& a_key, C* a_val) const
358  {
359  const SPInt index = FindIndexAlt(a_key);
360  if (index >= 0) {
361  if (a_val) {
362  *a_val = E(index).value;
363  }
364  return true;
365  } else {
366  return false;
367  }
368  }
369 
370  [[nodiscard]] UPInt GetSize() const
371  {
372  return table ? 0 : (UPInt)table->entryCount;
373  }
374 
375  void CheckExpand(void* a_memAddr)
376  {
377  if (!table) {
378  SetRawCapacity(a_memAddr, HashMinSize);
379  } else if (table->entryCount * 5 > (table->sizeMask + 1) * 4) {
380  SetRawCapacity(a_memAddr, (table->sizeMask + 1) * 2);
381  }
382  }
383 
384  void Resize(void* a_memAddr, UPInt a_count)
385  {
386  SetCapacity(a_memAddr, a_count);
387  }
388 
389  void SetCapacity(void* a_memAddr, UPInt a_newSize)
390  {
391  UPInt newRawSize = (a_newSize * 5) / 4;
392  if (newRawSize <= GetSize()) {
393  return;
394  }
395  SetRawCapacity(a_memAddr, newRawSize);
396  }
397 
399  {
400  if (table == 0) {
401  return iterator(0, 0);
402  }
403 
404  UPInt idx = 0;
405  while (idx <= table->sizeMask && E(idx).IsEmpty()) {
406  ++idx;
407  }
408  return iterator(this, idx);
409  }
410 
412  {
413  return iterator(0, 0);
414  }
415 
417  {
418  return const_cast<SelfType*>(this)->begin();
419  }
420 
422  {
423  return const_cast<SelfType*>(this)->end();
424  }
425 
426  template <class K>
427  iterator Find(const K& a_key)
428  {
429  SPInt index = FindIndex(a_key);
430  if (index >= 0) {
431  return iterator(this, index);
432  }
433  return iterator(0, 0);
434  }
435 
436  template <class K>
437  iterator FindAlt(const K& a_key)
438  {
439  SPInt index = FindIndexAlt(a_key);
440  if (index >= 0) {
441  return iterator(this, index);
442  }
443  return iterator(0, 0);
444  }
445 
446  template <class K>
447  const_iterator Find(const K& a_key) const
448  {
449  return const_cast<SelfType*>(this)->Find(a_key);
450  }
451 
452  template <class K>
453  const_iterator FindAlt(const K& a_key) const
454  {
455  return const_cast<SelfType*>(this)->FindAlt(a_key);
456  }
457 
458  private:
459  enum
460  {
461  HashMinSize = 8
462  };
463 
464  struct TableType
465  {
466  UPInt entryCount; // 00
467  UPInt sizeMask; // 08
468  //Entry entries[0]; // 10
469  };
470  static_assert(sizeof(TableType) == 0x10);
471 
472  template <class K>
473  SPInt FindIndex(const K& a_key) const
474  {
475  if (!table) {
476  return -1;
477  }
478  UPInt hashValue = HashF()(a_key) & table->sizeMask;
479  return FindIndexCore(a_key, hashValue);
480  }
481 
482  template <class K>
483  SPInt FindIndexAlt(const K& a_key) const
484  {
485  if (!table) {
486  return -1;
487  }
488  const UPInt hashValue = AltHashF()(a_key) & table->sizeMask;
489  return FindIndexCore(a_key, hashValue);
490  }
491 
492  template <class K>
493  SPInt FindIndexCore(const K& a_key, UPInt a_hashValue) const
494  {
495  assert(table);
496  assert((a_hashValue & ~table->sizeMask) == 0);
497 
498  UPInt index = a_hashValue;
499  const Entry* entry = std::addressof(E(index));
500 
501  if (entry->IsEmpty() || (entry->GetCachedHash(table->sizeMask) != index)) {
502  return -1;
503  }
504 
505  for (;;) {
506  assert(entry->GetCachedHash(table->sizeMask) == a_hashValue);
507  if (entry->GetCachedHash(table->sizeMask) == a_hashValue && entry->value == a_key) {
508  return index;
509  }
510  assert(!(entry->value == a_key));
511 
512  index = entry->nextInChain;
513  if (index == (UPInt)-1) {
514  break;
515  }
516 
517  entry = std::addressof(E(index));
518  assert(!entry->IsEmpty());
519  }
520  return -1;
521  }
522 
523  template <class CRef>
524  void Add(void* a_memAddr, const CRef& a_key, UPInt a_hashValue)
525  {
526  CheckExpand(a_memAddr);
527  a_hashValue &= table->sizeMask;
528 
529  ++(table->entryCount);
530 
531  const SPInt index = a_hashValue;
532  Entry* naturalEntry = &(E(index));
533 
534  if (naturalEntry->IsEmpty()) {
535  new (naturalEntry) Entry(a_key, -1);
536  } else {
537  SPInt blankIndex = index;
538  do {
539  blankIndex = (blankIndex + 1) & table->sizeMask;
540  } while (!E(blankIndex).IsEmpty());
541 
542  Entry* blankEntry = std::addressof(E(blankIndex));
543 
544  if (naturalEntry->GetCachedHash(table->sizeMask) == (UPInt)index) {
545  new (blankEntry) Entry(*naturalEntry);
546  naturalEntry->value = a_key;
547  naturalEntry->nextInChain = blankIndex;
548  } else {
549  SPInt collidedIndex = naturalEntry->GetCachedHash(table->sizeMask);
550  assert(collidedIndex >= 0 && collidedIndex <= (SPInt)table->sizeMask);
551  for (;;) {
552  Entry* entry = std::addressof(E(collidedIndex));
553  if (entry->nextInChain == index) {
554  new (blankEntry) Entry(*naturalEntry);
555  entry->nextInChain = blankIndex;
556  break;
557  }
558  collidedIndex = entry->nextInChain;
559  assert(collidedIndex >= 0 && collidedIndex <= (SPInt)table->sizeMask);
560  }
561 
562  naturalEntry->value = a_key;
563  naturalEntry->nextInChain = -1;
564  }
565  }
566 
567  naturalEntry->SetCachedHash(a_hashValue);
568  }
569 
570  Entry& E(UPInt a_index)
571  {
572  assert(a_index <= table->sizeMask);
573  return *(((Entry*)(table + 1)) + a_index);
574  }
575 
576  const Entry& E(UPInt a_index) const
577  {
578  assert(a_index <= table->sizeMask);
579  return *(((Entry*)(table + 1)) + a_index);
580  }
581 
582  void SetRawCapacity(void* a_heapAddr, UPInt a_newSize)
583  {
584  if (a_newSize == 0) {
585  Clear();
586  return;
587  }
588 
589  if (a_newSize < HashMinSize) {
590  a_newSize = HashMinSize;
591  } else {
592  const auto bits = gfchop(glog2f((float)(a_newSize - 1)) + 1);
593  a_newSize = UPInt(1) << bits;
594  }
595 
596  SelfType newHash;
597  newHash.table = static_cast<TableType*>(Allocator::Alloc(a_heapAddr, sizeof(TableType) + sizeof(Entry) * a_newSize));
598  assert(newHash.table);
599 
600  newHash.table->entryCount = 0;
601  newHash.table->sizeMask = a_newSize - 1;
602  UPInt i;
603  UPInt n;
604 
605  for (i = 0; i < a_newSize; i++) {
606  newHash.E(i).nextInChain = -2;
607  }
608 
609  if (table) {
610  for (i = 0, n = table->sizeMask; i <= n; ++i) {
611  Entry* entry = std::addressof(E(i));
612  if (entry->IsEmpty() == false) {
613  newHash.Add(a_heapAddr, entry->value);
614  entry->Clear();
615  }
616  }
617  Allocator::Free(table);
618  }
619 
620  // Steal newHash's data.
621  table = newHash.table;
622  newHash.table = nullptr;
623  }
624 
625  // members
626  TableType* table; // 00
627  };
628  // size == 0x8
629 }
Definition: GHashSetBase.h:18
iterator begin()
Definition: GHashSetBase.h:398
const_iterator Find(const K &a_key) const
Definition: GHashSetBase.h:447
GHashSetBase(std::int32_t a_sizeHint)
Definition: GHashSetBase.h:169
GHashSetBase(void *a_memAddr, std::int32_t a_sizeHint)
Definition: GHashSetBase.h:179
bool IsEmpty() const
Definition: GHashSetBase.h:235
iterator FindAlt(const K &a_key)
Definition: GHashSetBase.h:437
void Set(void *a_memAddr, const CRef &a_key)
Definition: GHashSetBase.h:241
const C * Get(const K &key) const
Definition: GHashSetBase.h:326
const_iterator begin() const
Definition: GHashSetBase.h:416
~GHashSetBase()
Definition: GHashSetBase.h:191
void Remove(const CRef &a_key)
Definition: GHashSetBase.h:309
iterator Find(const K &a_key)
Definition: GHashSetBase.h:427
friend struct iterator
Definition: GHashSetBase.h:163
const C * GetAlt(const K &a_key) const
Definition: GHashSetBase.h:346
void Resize(void *a_memAddr, UPInt a_count)
Definition: GHashSetBase.h:384
UPInt GetSize() const
Definition: GHashSetBase.h:370
iterator end()
Definition: GHashSetBase.h:411
void SetCapacity(void *a_memAddr, UPInt a_newSize)
Definition: GHashSetBase.h:389
void Clear()
Definition: GHashSetBase.h:220
GHashSetBase< C, HashF, AltHashF, Allocator, Entry > SelfType
Definition: GHashSetBase.h:20
void RemoveAlt(const K &a_key)
Definition: GHashSetBase.h:265
C * Get(const K &a_key)
Definition: GHashSetBase.h:315
GHashSetBase([[maybe_unused]] void *a_memAddr)
Definition: GHashSetBase.h:175
C * GetAlt(const K &a_key)
Definition: GHashSetBase.h:337
bool GetAlt(const K &a_key, C *a_val) const
Definition: GHashSetBase.h:357
GFC_MEMORY_REDEFINE_NEW(GHashSetBase, Allocator::kStatID)
void Assign(void *a_memAddr, const SelfType &a_src)
Definition: GHashSetBase.h:209
void Add(void *a_memAddr, const CRef &a_key)
Definition: GHashSetBase.h:258
const_iterator end() const
Definition: GHashSetBase.h:421
GHashSetBase(const SelfType &a_src)
Definition: GHashSetBase.h:185
const_iterator FindAlt(const K &a_key) const
Definition: GHashSetBase.h:453
GHashSetBase()
Definition: GHashSetBase.h:165
void CheckExpand(void *a_memAddr)
Definition: GHashSetBase.h:375
Definition: AbsorbEffect.h:6
constexpr std::int32_t gfchop(float a_val)
Definition: GMath.h:7
std::size_t UPInt
Definition: SFTypes.h:5
std::ptrdiff_t SPInt
Definition: SFTypes.h:8
float glog2f(float a_val)
Definition: GMath.h:12
Definition: GHashSetBase.h:23
const_iterator()
Definition: GHashSetBase.h:25
void operator++()
Definition: GHashSetBase.h:42
const_iterator(const SelfType *a_hash, SPInt a_index)
Definition: GHashSetBase.h:74
bool IsEnd() const
Definition: GHashSetBase.h:66
const SelfType * hash
Definition: GHashSetBase.h:79
SPInt index
Definition: GHashSetBase.h:80
bool operator==(const const_iterator &a_it) const
Definition: GHashSetBase.h:52
bool operator!=(const const_iterator &a_it) const
Definition: GHashSetBase.h:61
const C & operator*() const
Definition: GHashSetBase.h:30
const C * operator->() const
Definition: GHashSetBase.h:36
Definition: GHashSetBase.h:87
iterator()
Definition: GHashSetBase.h:89
C & operator*() const
Definition: GHashSetBase.h:93
void Remove()
Definition: GHashSetBase.h:104
C * operator->() const
Definition: GHashSetBase.h:99