Skip to content

Commit d7efb2f

Browse files
committed
Add Heap and HashTable
1 parent 0ca638c commit d7efb2f

File tree

6 files changed

+799
-0
lines changed

6 files changed

+799
-0
lines changed
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
//
2+
// HashTable.cpp
3+
// OpenGraphCxx
4+
//
5+
// Status: Complete
6+
// Modified based Compute code
7+
8+
#include <OpenGraphCxx/Util/HashTable.hpp>
9+
#include <OpenGraphCxx/Util/Heap.hpp>
10+
#include <memory>
11+
12+
namespace util {
13+
14+
uint64_t pointer_hash(void const *pointer) {
15+
int64_t result = (-1 ^ (int64_t)(pointer) << 0x20) + (int64_t)pointer;
16+
result = result ^ result >> 0x16;
17+
result = result + (-1 ^ result << 0xd);
18+
result = (result ^ result >> 8) * 9;
19+
result = result ^ result >> 0xf;
20+
result = result + (-1 ^ result << 0x1b);
21+
return result ^ result >> 0x1f;
22+
}
23+
24+
bool pointer_compare(void const *a, void const *b) { return a == b; }
25+
26+
uint64_t string_hash(char const *str) {
27+
int64_t result = 0;
28+
for (char const *c = str; *c; c += 1) {
29+
result = result * 33 + *c;
30+
}
31+
return result;
32+
}
33+
34+
constexpr uint32_t initial_bucket_mask_width = 4;
35+
36+
UntypedTable *UntypedTable::create() { return new UntypedTable(); }
37+
38+
void UntypedTable::destroy(UntypedTable *value) { delete value; }
39+
40+
UntypedTable::UntypedTable() {
41+
_hash = pointer_hash;
42+
_compare = pointer_compare;
43+
_did_remove_key = nullptr;
44+
_did_remove_value = nullptr;
45+
_heap = nullptr;
46+
_spare_node = 0;
47+
_buckets = nullptr;
48+
_count = 0;
49+
_bucket_mask = 0;
50+
_bucket_mask_width = 0;
51+
_is_heap_owner = true;
52+
_compare_by_pointer = true;
53+
}
54+
55+
UntypedTable::UntypedTable(hasher custom_hash, key_equal custom_compare, key_callback did_remove_key,
56+
value_callback did_remove_value, Heap *heap) {
57+
_hash = custom_hash != nullptr ? custom_hash : pointer_hash;
58+
_compare = custom_compare != nullptr ? custom_compare : pointer_compare;
59+
_did_remove_key = did_remove_key;
60+
_did_remove_value = did_remove_value;
61+
_heap = heap;
62+
_spare_node = 0;
63+
_buckets = nullptr;
64+
_count = 0;
65+
_bucket_mask = 0;
66+
_bucket_mask_width = 0;
67+
_is_heap_owner = heap == nullptr;
68+
_compare_by_pointer = custom_compare == nullptr || custom_compare == pointer_compare;
69+
}
70+
71+
UntypedTable::~UntypedTable() {
72+
if ((_did_remove_key || _did_remove_value) && _count) {
73+
for (uint32_t bucket = 0; !(bucket >> _bucket_mask_width); bucket++) {
74+
for (HashNode *node = _buckets[bucket]; node != nullptr; node = node->next) {
75+
if (_did_remove_key) {
76+
_did_remove_key(node->key);
77+
}
78+
if (_did_remove_value) {
79+
_did_remove_value(node->value);
80+
}
81+
}
82+
}
83+
}
84+
if (_bucket_mask_width > initial_bucket_mask_width) {
85+
free(_buckets);
86+
}
87+
if (_is_heap_owner && _heap) {
88+
delete _heap;
89+
}
90+
}
91+
92+
#pragma mark - Managing buckets
93+
94+
// Buckets are initially allocated by the util::Heap instance,
95+
// until they grow past initial_bucket_mask_width where they are allocated using new.ˆ
96+
97+
void UntypedTable::create_buckets() {
98+
if (_buckets != nullptr) {
99+
return;
100+
}
101+
102+
_bucket_mask_width = initial_bucket_mask_width;
103+
_bucket_mask = (1 << initial_bucket_mask_width) - 1;
104+
105+
if (_heap == nullptr) {
106+
_heap = new Heap(nullptr, 0, Heap::minimum_increment);
107+
}
108+
109+
size_t num_buckets = (1 << initial_bucket_mask_width);
110+
_buckets = _heap->alloc<Bucket>(num_buckets);
111+
std::memset(_buckets, 0, sizeof(Bucket) * num_buckets);
112+
}
113+
114+
void UntypedTable::grow_buckets() {
115+
if (_bucket_mask_width > 30) {
116+
return;
117+
}
118+
119+
uint32_t old_width = _bucket_mask_width;
120+
_bucket_mask_width = old_width + 1;
121+
122+
Bucket *old_buckets = _buckets;
123+
Bucket *new_buckets = nullptr;
124+
125+
size_t num_buckets = 1 << _bucket_mask_width;
126+
if (_bucket_mask_width > initial_bucket_mask_width) {
127+
new_buckets = new Bucket[num_buckets];
128+
} else {
129+
if (_heap == nullptr) {
130+
_heap = new Heap(nullptr, 0, Heap::minimum_increment);
131+
}
132+
new_buckets = _heap->alloc<Bucket>(num_buckets);
133+
}
134+
std::memset(new_buckets, 0, sizeof(Bucket) * num_buckets);
135+
136+
// redistribute old buckets into new
137+
if (new_buckets) {
138+
_bucket_mask = num_buckets - 1;
139+
for (uint32_t i = 0; !(i >> old_width); i++) {
140+
for (UntypedTable::HashNode *node = old_buckets[i]; node != nullptr; node = node->next) {
141+
uint64_t new_bucket = _bucket_mask & node->hash_value;
142+
node->next = new_buckets[new_bucket];
143+
new_buckets[new_bucket] = node;
144+
}
145+
}
146+
_buckets = new_buckets;
147+
148+
if (old_width > initial_bucket_mask_width) {
149+
delete old_buckets;
150+
}
151+
}
152+
}
153+
154+
#pragma mark - Lookup
155+
156+
UntypedTable::value_type UntypedTable::lookup(key_type key, nullable_key_type *found_key_out) const noexcept {
157+
if (_count) {
158+
uint64_t hash_value = _hash(key);
159+
HashNode *node = _buckets[_bucket_mask & hash_value];
160+
if (_compare_by_pointer) {
161+
for (; node != nullptr; node = node->next) {
162+
if (node->key == key) {
163+
if (found_key_out) {
164+
*found_key_out = node->key;
165+
}
166+
return node->value;
167+
}
168+
}
169+
} else if (node) {
170+
for (; node != nullptr; node = node->next) {
171+
if (node->hash_value == hash_value && _compare(node->key, key)) {
172+
if (found_key_out) {
173+
*found_key_out = node->key;
174+
}
175+
return node->value;
176+
}
177+
}
178+
}
179+
}
180+
if (found_key_out) {
181+
*found_key_out = nullptr;
182+
}
183+
return nullptr;
184+
}
185+
186+
#pragma mark - Modifying entries
187+
188+
bool UntypedTable::insert(key_type key, value_type value) {
189+
if (_buckets == nullptr) {
190+
this->create_buckets();
191+
}
192+
193+
void *result;
194+
195+
uint64_t hash_value = _hash(key);
196+
HashNode *node = _buckets[hash_value & _bucket_mask];
197+
198+
// replace existing if match
199+
for (; node != nullptr; node = node->next) {
200+
if (node->hash_value == hash_value && _compare(node->key, key)) {
201+
if (_did_remove_key) {
202+
_did_remove_key(node->key);
203+
}
204+
if (_did_remove_value) {
205+
_did_remove_value(node->value);
206+
}
207+
node->key = key;
208+
node->value = value;
209+
return false;
210+
}
211+
}
212+
213+
// insert new
214+
if (_count + 1 > 4 << _bucket_mask_width) {
215+
this->grow_buckets();
216+
}
217+
if (!_heap) {
218+
_heap = new Heap(nullptr, 0, Heap::minimum_increment);
219+
}
220+
221+
HashNode *inserted_node = _spare_node;
222+
if (inserted_node) {
223+
_spare_node = _spare_node->next;
224+
} else {
225+
inserted_node = _heap->alloc<HashNode>();
226+
}
227+
228+
inserted_node->key = key;
229+
inserted_node->value = value;
230+
inserted_node->hash_value = hash_value;
231+
232+
uint64_t bucket = _bucket_mask & hash_value;
233+
inserted_node->next = _buckets[bucket];
234+
_buckets[bucket] = inserted_node;
235+
236+
_count += 1;
237+
238+
return true;
239+
}
240+
241+
bool UntypedTable::remove(key_type key) {
242+
if (_count == 0) {
243+
return false;
244+
}
245+
if (_compare_by_pointer) {
246+
return this->remove_ptr(key);
247+
}
248+
uint64_t hash_value = _hash(key);
249+
HashNode *node = _buckets[_bucket_mask & hash_value];
250+
251+
for (HashNode *candidate = node; candidate != nullptr; candidate = candidate->next) {
252+
if (candidate->hash_value == hash_value && _compare(candidate->key, key)) {
253+
node->next = candidate->next;
254+
if (_did_remove_key) {
255+
_did_remove_key(candidate->key);
256+
}
257+
if (_did_remove_value) {
258+
_did_remove_value(candidate->value);
259+
}
260+
candidate->next = _spare_node;
261+
_spare_node = candidate;
262+
_count -= 1;
263+
return true;
264+
}
265+
node = candidate;
266+
}
267+
268+
return false;
269+
}
270+
271+
bool UntypedTable::remove_ptr(key_type key) {
272+
if (_count == 0) {
273+
return false;
274+
}
275+
276+
HashNode *node = _buckets[_bucket_mask & _hash(key)];
277+
for (HashNode *candidate = node; candidate != nullptr; candidate = candidate->next) {
278+
if (candidate->key == key) {
279+
node->next = candidate->next;
280+
if (_did_remove_key) {
281+
_did_remove_key(candidate->key);
282+
}
283+
if (_did_remove_value) {
284+
_did_remove_value(candidate->value);
285+
}
286+
candidate->next = _spare_node;
287+
_spare_node = candidate;
288+
_count -= 1;
289+
return true;
290+
}
291+
node = candidate;
292+
}
293+
return false;
294+
}
295+
296+
void UntypedTable::for_each(entry_callback body, void *context) const {
297+
if (_count) {
298+
for (uint32_t i = 0; !(i >> _bucket_mask_width); i++) {
299+
for (UntypedTable::HashNode *node = _buckets[i]; node != nullptr; node = node->next) {
300+
body(node->key, node->value, context);
301+
}
302+
}
303+
}
304+
}
305+
306+
} /* namespace util */

0 commit comments

Comments
 (0)