Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Common/include/Luau/DenseHash.h
2727 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#pragma once
3
4
#include "Luau/HashUtil.h"
5
#include "Luau/Common.h"
6
7
#include <stddef.h>
8
#include <functional>
9
#include <utility>
10
#include <stdint.h>
11
12
namespace Luau
13
{
14
15
// Internal implementation of DenseHashSet and DenseHashMap
16
namespace detail
17
{
18
19
template<typename Key, typename Item, typename MutableItem, typename ItemInterface, typename Hash, typename Eq>
20
class DenseHashTable
21
{
22
public:
23
class const_iterator;
24
class iterator;
25
26
explicit DenseHashTable(const Key& empty_key, size_t buckets = 0)
27
: data(nullptr)
28
, capacity(0)
29
, count(0)
30
, empty_key(empty_key)
31
{
32
// validate that equality operator is at least somewhat functional
33
LUAU_ASSERT(eq(empty_key, empty_key));
34
// buckets has to be power-of-two or zero
35
LUAU_ASSERT((buckets & (buckets - 1)) == 0);
36
37
if (buckets)
38
{
39
data = static_cast<Item*>(::operator new(sizeof(Item) * buckets));
40
capacity = buckets;
41
42
ItemInterface::fill(data, buckets, empty_key);
43
}
44
}
45
46
~DenseHashTable()
47
{
48
if (data)
49
destroy();
50
}
51
52
DenseHashTable(const DenseHashTable& other)
53
: data(nullptr)
54
, capacity(0)
55
, count(other.count)
56
, empty_key(other.empty_key)
57
{
58
if (other.capacity)
59
{
60
data = static_cast<Item*>(::operator new(sizeof(Item) * other.capacity));
61
62
for (size_t i = 0; i < other.capacity; ++i)
63
{
64
new (&data[i]) Item(other.data[i]);
65
capacity = i + 1; // if Item copy throws, capacity will note the number of initialized objects for destroy() to clean up
66
}
67
}
68
}
69
70
DenseHashTable(DenseHashTable&& other)
71
: data(other.data)
72
, capacity(other.capacity)
73
, count(other.count)
74
, empty_key(other.empty_key)
75
{
76
other.data = nullptr;
77
other.capacity = 0;
78
other.count = 0;
79
}
80
81
DenseHashTable& operator=(DenseHashTable&& other)
82
{
83
if (this != &other)
84
{
85
if (data)
86
destroy();
87
88
data = other.data;
89
capacity = other.capacity;
90
count = other.count;
91
empty_key = other.empty_key;
92
93
other.data = nullptr;
94
other.capacity = 0;
95
other.count = 0;
96
}
97
98
return *this;
99
}
100
101
DenseHashTable& operator=(const DenseHashTable& other)
102
{
103
if (this != &other)
104
{
105
DenseHashTable copy(other);
106
*this = std::move(copy);
107
}
108
109
return *this;
110
}
111
112
void clear(size_t thresholdToDestroy = 32)
113
{
114
if (count == 0)
115
return;
116
117
if (capacity > thresholdToDestroy)
118
{
119
destroy();
120
}
121
else
122
{
123
ItemInterface::destroy(data, capacity);
124
ItemInterface::fill(data, capacity, empty_key);
125
}
126
127
count = 0;
128
}
129
130
void destroy()
131
{
132
ItemInterface::destroy(data, capacity);
133
134
::operator delete(data);
135
data = nullptr;
136
137
capacity = 0;
138
}
139
140
Item* insert_unsafe(const Key& key)
141
{
142
// It is invalid to insert empty_key into the table since it acts as a "entry does not exist" marker
143
LUAU_ASSERT(!eq(key, empty_key));
144
145
size_t hashmod = capacity - 1;
146
size_t bucket = hasher(key) & hashmod;
147
148
for (size_t probe = 0; probe <= hashmod; ++probe)
149
{
150
Item& probe_item = data[bucket];
151
152
// Element does not exist, insert here
153
if (eq(ItemInterface::getKey(probe_item), empty_key))
154
{
155
ItemInterface::setKey(probe_item, key);
156
count++;
157
return &probe_item;
158
}
159
160
// Element already exists
161
if (eq(ItemInterface::getKey(probe_item), key))
162
{
163
return &probe_item;
164
}
165
166
// Hash collision, quadratic probing
167
bucket = (bucket + probe + 1) & hashmod;
168
}
169
170
// Hash table is full - this should not happen
171
LUAU_ASSERT(false);
172
return NULL;
173
}
174
175
const Item* find(const Key& key) const
176
{
177
if (count == 0)
178
return 0;
179
if (eq(key, empty_key))
180
return 0;
181
182
size_t hashmod = capacity - 1;
183
size_t bucket = hasher(key) & hashmod;
184
185
for (size_t probe = 0; probe <= hashmod; ++probe)
186
{
187
const Item& probe_item = data[bucket];
188
189
// Element exists
190
if (eq(ItemInterface::getKey(probe_item), key))
191
return &probe_item;
192
193
// Element does not exist
194
if (eq(ItemInterface::getKey(probe_item), empty_key))
195
return NULL;
196
197
// Hash collision, quadratic probing
198
bucket = (bucket + probe + 1) & hashmod;
199
}
200
201
// Hash table is full - this should not happen
202
LUAU_ASSERT(false);
203
return NULL;
204
}
205
206
void rehash()
207
{
208
size_t newsize = capacity == 0 ? 16 : capacity * 2;
209
210
DenseHashTable newtable(empty_key, newsize);
211
212
for (size_t i = 0; i < capacity; ++i)
213
{
214
const Key& key = ItemInterface::getKey(data[i]);
215
216
if (!eq(key, empty_key))
217
{
218
Item* item = newtable.insert_unsafe(key);
219
*item = std::move(data[i]);
220
}
221
}
222
223
LUAU_ASSERT(count == newtable.count);
224
225
std::swap(data, newtable.data);
226
std::swap(capacity, newtable.capacity);
227
}
228
229
void rehash_if_full(const Key& key)
230
{
231
if (count >= capacity * 3 / 4 && !find(key))
232
{
233
rehash();
234
}
235
}
236
237
const_iterator begin() const
238
{
239
size_t start = 0;
240
241
while (start < capacity && eq(ItemInterface::getKey(data[start]), empty_key))
242
start++;
243
244
return const_iterator(this, start);
245
}
246
247
const_iterator end() const
248
{
249
return const_iterator(this, capacity);
250
}
251
252
iterator begin()
253
{
254
size_t start = 0;
255
256
while (start < capacity && eq(ItemInterface::getKey(data[start]), empty_key))
257
start++;
258
259
return iterator(this, start);
260
}
261
262
iterator end()
263
{
264
return iterator(this, capacity);
265
}
266
267
size_t size() const
268
{
269
return count;
270
}
271
272
class const_iterator
273
{
274
public:
275
using value_type = Item;
276
using reference = Item&;
277
using pointer = Item*;
278
using difference_type = ptrdiff_t;
279
using iterator_category = std::forward_iterator_tag;
280
281
const_iterator()
282
: set(0)
283
, index(0)
284
{
285
}
286
287
const_iterator(const DenseHashTable<Key, Item, MutableItem, ItemInterface, Hash, Eq>* set, size_t index)
288
: set(set)
289
, index(index)
290
{
291
}
292
293
const Item& operator*() const
294
{
295
return set->data[index];
296
}
297
298
const Item* operator->() const
299
{
300
return &set->data[index];
301
}
302
303
bool operator==(const const_iterator& other) const
304
{
305
return set == other.set && index == other.index;
306
}
307
308
bool operator!=(const const_iterator& other) const
309
{
310
return set != other.set || index != other.index;
311
}
312
313
const_iterator& operator++()
314
{
315
size_t size = set->capacity;
316
317
do
318
{
319
index++;
320
} while (index < size && set->eq(ItemInterface::getKey(set->data[index]), set->empty_key));
321
322
return *this;
323
}
324
325
const_iterator operator++(int)
326
{
327
const_iterator res = *this;
328
++*this;
329
return res;
330
}
331
332
private:
333
const DenseHashTable<Key, Item, MutableItem, ItemInterface, Hash, Eq>* set;
334
size_t index;
335
};
336
337
class iterator
338
{
339
public:
340
using value_type = MutableItem;
341
using reference = MutableItem&;
342
using pointer = MutableItem*;
343
using difference_type = ptrdiff_t;
344
using iterator_category = std::forward_iterator_tag;
345
346
iterator()
347
: set(0)
348
, index(0)
349
{
350
}
351
352
iterator(DenseHashTable<Key, Item, MutableItem, ItemInterface, Hash, Eq>* set, size_t index)
353
: set(set)
354
, index(index)
355
{
356
}
357
358
MutableItem& operator*() const
359
{
360
return *reinterpret_cast<MutableItem*>(&set->data[index]);
361
}
362
363
MutableItem* operator->() const
364
{
365
return reinterpret_cast<MutableItem*>(&set->data[index]);
366
}
367
368
bool operator==(const iterator& other) const
369
{
370
return set == other.set && index == other.index;
371
}
372
373
bool operator!=(const iterator& other) const
374
{
375
return set != other.set || index != other.index;
376
}
377
378
iterator& operator++()
379
{
380
size_t size = set->capacity;
381
382
do
383
{
384
index++;
385
} while (index < size && set->eq(ItemInterface::getKey(set->data[index]), set->empty_key));
386
387
return *this;
388
}
389
390
iterator operator++(int)
391
{
392
iterator res = *this;
393
++*this;
394
return res;
395
}
396
397
private:
398
DenseHashTable<Key, Item, MutableItem, ItemInterface, Hash, Eq>* set;
399
size_t index;
400
};
401
402
private:
403
Item* data;
404
size_t capacity;
405
size_t count;
406
Key empty_key;
407
Hash hasher;
408
Eq eq;
409
};
410
411
template<typename Key>
412
struct ItemInterfaceSet
413
{
414
static const Key& getKey(const Key& item)
415
{
416
return item;
417
}
418
419
static void setKey(Key& item, const Key& key)
420
{
421
item = key;
422
}
423
424
static void fill(Key* data, size_t count, const Key& key)
425
{
426
for (size_t i = 0; i < count; ++i)
427
new (&data[i]) Key(key);
428
}
429
430
static void destroy(Key* data, size_t count)
431
{
432
for (size_t i = 0; i < count; ++i)
433
data[i].~Key();
434
}
435
};
436
437
template<typename Key, typename Value>
438
struct ItemInterfaceMap
439
{
440
static const Key& getKey(const std::pair<Key, Value>& item)
441
{
442
return item.first;
443
}
444
445
static void setKey(std::pair<Key, Value>& item, const Key& key)
446
{
447
item.first = key;
448
}
449
450
static void fill(std::pair<Key, Value>* data, size_t count, const Key& key)
451
{
452
for (size_t i = 0; i < count; ++i)
453
{
454
new (&data[i].first) Key(key);
455
new (&data[i].second) Value();
456
}
457
}
458
459
static void destroy(std::pair<Key, Value>* data, size_t count)
460
{
461
for (size_t i = 0; i < count; ++i)
462
{
463
data[i].first.~Key();
464
data[i].second.~Value();
465
}
466
}
467
};
468
469
} // namespace detail
470
471
// This is a faster alternative of unordered_set, but it does not implement the same interface (i.e. it does not support erasing)
472
template<typename Key, typename Hash = detail::DenseHashDefault<Key>, typename Eq = std::equal_to<Key>>
473
class DenseHashSet
474
{
475
typedef detail::DenseHashTable<Key, Key, Key, detail::ItemInterfaceSet<Key>, Hash, Eq> Impl;
476
Impl impl;
477
478
public:
479
typedef typename Impl::const_iterator const_iterator;
480
typedef typename Impl::iterator iterator;
481
482
explicit DenseHashSet(const Key& empty_key, size_t buckets = 0)
483
: impl(empty_key, buckets)
484
{
485
}
486
487
void clear()
488
{
489
impl.clear();
490
}
491
492
const Key& insert(const Key& key)
493
{
494
impl.rehash_if_full(key);
495
return *impl.insert_unsafe(key);
496
}
497
498
const Key* find(const Key& key) const
499
{
500
return impl.find(key);
501
}
502
503
bool contains(const Key& key) const
504
{
505
return impl.find(key) != 0;
506
}
507
508
size_t size() const
509
{
510
return impl.size();
511
}
512
513
bool empty() const
514
{
515
return impl.size() == 0;
516
}
517
518
const_iterator begin() const
519
{
520
return impl.begin();
521
}
522
523
const_iterator end() const
524
{
525
return impl.end();
526
}
527
528
iterator begin()
529
{
530
return impl.begin();
531
}
532
533
iterator end()
534
{
535
return impl.end();
536
}
537
538
bool operator==(const DenseHashSet<Key, Hash, Eq>& other) const
539
{
540
if (size() != other.size())
541
return false;
542
543
for (const Key& k : *this)
544
{
545
if (!other.contains(k))
546
return false;
547
}
548
549
return true;
550
}
551
552
bool operator!=(const DenseHashSet<Key, Hash, Eq>& other) const
553
{
554
return !(*this == other);
555
}
556
};
557
558
// This is a faster alternative of unordered_map, but it does not implement the same interface (i.e. it does not support erasing and has
559
// contains() instead of find())
560
template<typename Key, typename Value, typename Hash = detail::DenseHashDefault<Key>, typename Eq = std::equal_to<Key>>
561
class DenseHashMap
562
{
563
typedef detail::DenseHashTable<Key, std::pair<Key, Value>, std::pair<const Key, Value>, detail::ItemInterfaceMap<Key, Value>, Hash, Eq> Impl;
564
Impl impl;
565
566
public:
567
typedef typename Impl::const_iterator const_iterator;
568
typedef typename Impl::iterator iterator;
569
570
explicit DenseHashMap(const Key& empty_key, size_t buckets = 0)
571
: impl(empty_key, buckets)
572
{
573
}
574
575
void clear(size_t thresholdToDestroy = 32)
576
{
577
impl.clear(thresholdToDestroy);
578
}
579
580
// Note: this reference is invalidated by any insert operation (i.e. operator[])
581
Value& operator[](const Key& key)
582
{
583
impl.rehash_if_full(key);
584
return impl.insert_unsafe(key)->second;
585
}
586
587
// Note: this pointer is invalidated by any insert operation (i.e. operator[])
588
const Value* find(const Key& key) const
589
{
590
const std::pair<Key, Value>* result = impl.find(key);
591
592
return result ? &result->second : NULL;
593
}
594
595
// Note: this pointer is invalidated by any insert operation (i.e. operator[])
596
Value* find(const Key& key)
597
{
598
const std::pair<Key, Value>* result = impl.find(key);
599
600
return result ? const_cast<Value*>(&result->second) : NULL;
601
}
602
603
bool contains(const Key& key) const
604
{
605
return impl.find(key) != 0;
606
}
607
608
std::pair<Value&, bool> try_insert(const Key& key, const Value& value)
609
{
610
impl.rehash_if_full(key);
611
612
size_t before = impl.size();
613
std::pair<Key, Value>* slot = impl.insert_unsafe(key);
614
615
// Value is fresh if container count has increased
616
bool fresh = impl.size() > before;
617
618
if (fresh)
619
slot->second = value;
620
621
return std::make_pair(std::ref(slot->second), fresh);
622
}
623
624
std::pair<Value&, bool> try_insert(const Key& key, Value&& value)
625
{
626
impl.rehash_if_full(key);
627
628
size_t before = impl.size();
629
std::pair<Key, Value>* slot = impl.insert_unsafe(key);
630
631
// Value is fresh if container count has increased
632
bool fresh = impl.size() > before;
633
634
if (fresh)
635
slot->second = std::move(value);
636
637
return std::make_pair(std::ref(slot->second), fresh);
638
}
639
640
size_t size() const
641
{
642
return impl.size();
643
}
644
645
bool empty() const
646
{
647
return impl.size() == 0;
648
}
649
650
const_iterator begin() const
651
{
652
return impl.begin();
653
}
654
655
const_iterator end() const
656
{
657
return impl.end();
658
}
659
660
iterator begin()
661
{
662
return impl.begin();
663
}
664
665
iterator end()
666
{
667
return impl.end();
668
}
669
};
670
671
} // namespace Luau
672
673