-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhashtable.cpp
More file actions
140 lines (122 loc) · 4.06 KB
/
hashtable.cpp
File metadata and controls
140 lines (122 loc) · 4.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <assert.h>
#include <stdlib.h> // calloc(), free()
#include "hashtable.h"
// n must be a power of 2
static void h_init(HTab *htab, size_t n) {
assert(n > 0 && ((n - 1) & n) == 0);
htab->tab = (HNode **)calloc(n, sizeof(HNode *));
htab->mask = n - 1;
htab->size = 0;
}
// hashtable insertion
static void h_insert(HTab *htab, HNode *node) {
size_t pos = node->hcode & htab->mask;
HNode *next = htab->tab[pos];
node->next = next;
htab->tab[pos] = node;
htab->size++;
}
// hashtable look up subroutine.
// Pay attention to the return value. It returns the address of
// the parent pointer that owns the target node,
// which can be used to delete the target node.
static HNode **h_lookup(HTab *htab, HNode *key, bool (*eq)(HNode *, HNode *)) {
if (!htab->tab) {
return NULL;
}
size_t pos = key->hcode & htab->mask;
HNode **from = &htab->tab[pos]; // incoming pointer to the target
for (HNode *cur; (cur = *from) != NULL; from = &cur->next) {
if (cur->hcode == key->hcode && eq(cur, key)) {
return from; // may be a node, may be a slot
}
}
return NULL;
}
// remove a node from the chain
static HNode *h_detach(HTab *htab, HNode **from) {
HNode *node = *from; // the target node
*from = node->next; // update the incoming pointer to the target
htab->size--;
return node;
}
const size_t k_rehashing_work = 128; // constant work
static void hm_help_rehashing(HMap *hmap) {
size_t nwork = 0;
while (nwork < k_rehashing_work && hmap->older.size > 0) {
// find a non-empty slot
HNode **from = &hmap->older.tab[hmap->migrate_pos];
if (!*from) {
hmap->migrate_pos++;
continue; // empty slot
}
// move the first list item to the newer table
h_insert(&hmap->newer, h_detach(&hmap->older, from));
nwork++;
}
// discard the old table if done
if (hmap->older.size == 0 && hmap->older.tab) {
free(hmap->older.tab);
hmap->older = HTab{};
}
}
static void hm_trigger_rehashing(HMap *hmap) {
assert(hmap->older.tab == NULL);
// (newer, older) <- (new_table, newer)
hmap->older = hmap->newer;
h_init(&hmap->newer, (hmap->newer.mask + 1) * 2);
hmap->migrate_pos = 0;
}
HNode *hm_lookup(HMap *hmap, HNode *key, bool (*eq)(HNode *, HNode *)) {
hm_help_rehashing(hmap);
HNode **from = h_lookup(&hmap->newer, key, eq);
if (!from) {
from = h_lookup(&hmap->older, key, eq);
}
return from ? *from : NULL;
}
const size_t k_max_load_factor = 8;
void hm_insert(HMap *hmap, HNode *node) {
if (!hmap->newer.tab) {
h_init(&hmap->newer, 4); // initialize it if empty
}
h_insert(&hmap->newer, node); // always insert to the newer table
if (!hmap->older.tab) { // check whether we need to rehash
size_t shreshold = (hmap->newer.mask + 1) * k_max_load_factor;
if (hmap->newer.size >= shreshold) {
hm_trigger_rehashing(hmap);
}
}
hm_help_rehashing(hmap); // migrate some keys
}
HNode *hm_delete(HMap *hmap, HNode *key, bool (*eq)(HNode *, HNode *)) {
hm_help_rehashing(hmap);
if (HNode **from = h_lookup(&hmap->newer, key, eq)) {
return h_detach(&hmap->newer, from);
}
if (HNode **from = h_lookup(&hmap->older, key, eq)) {
return h_detach(&hmap->older, from);
}
return NULL;
}
void hm_clear(HMap *hmap) {
free(hmap->newer.tab);
free(hmap->older.tab);
*hmap = HMap{};
}
size_t hm_size(HMap *hmap) {
return hmap->newer.size + hmap->older.size;
}
static bool h_foreach(HTab *htab, bool (*f)(HNode *, void *), void *arg) {
for (size_t i = 0; htab->mask != 0 && i <= htab->mask; i++) {
for (HNode *node = htab->tab[i]; node != NULL; node = node->next) {
if (!f(node, arg)) {
return false;
}
}
}
return true;
}
void hm_foreach(HMap *hmap, bool (*f)(HNode *, void *), void *arg) {
h_foreach(&hmap->newer, f, arg) && h_foreach(&hmap->older, f, arg);
}