-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrobinhoodhash.h
172 lines (151 loc) · 5.88 KB
/
robinhoodhash.h
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#ifndef ROBINHOOD_HASH_H
#define ROBINHOOD_HASH_H
// Flexible C macros implementation of fixed size array-based Robin Hood hash table with backward-shift deletion.
// Implemented by Vitaly "_Vi" Shukela in 2017. License = MIT or Apache 2.0.
// Don't shrink this hash table by sequentially iterating it and inserting elements into a smaller table
// "X macro" pattern is expected to be for this parameters:
// "x" part is variable, substitude it with your identifier and specify it later as "tbl"
// #define x_setvalue(index, key, val) is a macro to set value to the table cell
// #define x_setnil(index) is a macro that marks table entry as empty
// #define x_swap(index1, index2) is a macro that swaps two entries in the table
// #define x_nilvalue is a value siganlizing that key is not found
// #define x_getvalue(index) is a macro to get value of the table cell
// #define x_getkey(index) is a macro for obtaining key for given index of the table.
// #define x_keysequal(key1,key2) is a macro that should return true when keys are equal
// #define x_isnil(index) is a macro that checks if table entry is empty
// #define x_n_elem should point to number of buckets in hash table
// #define x_getbucket(key) is a macro for obtaining adviced
// index of the table of the given key. Should be in range [1, n_elem[.
// #define x_overflow is a macro that is called when table is full
// #define x_removefailed(key) is a macro that called when trying to remove nonexisting element
// hash table entry should consist only of key and value and x_setvalue should completely set an entry
// If you don't need value just use reuse key as value
// API:
#define ROBINHOOD_HASH_SET(tbl, key, value) \
_ROBINHOOD_HASH_SET(tbl, key, value)
#define ROBINHOOD_HASH_GET(tbl, key, assignme) \
_ROBINHOOD_HASH_GET(tbl, key, assignme)
#define ROBINHOOD_HASH_DEL(tbl, key) \
_ROBINHOOD_HASH_DEL(tbl, key)
#define ROBINHOOD_HASH_SIZE(tbl, assignme) \
_ROBINHOOD_HASH_SIZE(tbl, assignme)
#define ROBINHOOD_HASH_CLEAR(tbl) \
_ROBINHOOD_HASH_CLEAR(tbl)
// Impl:
#include <stddef.h> // size_t
#define _ROBINHOOD_HASH_TYPICAL_INIT(key, getbucket) \
size_t _rh_i = getbucket(key); \
size_t _rh_i_orig = _rh_i; \
size_t _rh_temperature = 0;
#define _ROBINHOOD_HASH_TYPICAL_INCREMENT(breakcode, n_elem) \
_rh_temperature += 1; \
_rh_i += 1; \
if (_rh_i>=n_elem) _rh_i=1; \
if (_rh_i == _rh_i_orig) { \
breakcode \
break; \
}
#define _ROBINHOOD_HASH_DEBUGPRINT(tbl) { \
size_t _rh_i; \
printf("RBHDP: "); \
for(_rh_i=1; _rh_i < tbl##_n_elem; ++_rh_i) { \
printf("%02d:",_rh_i); \
if (tbl##_isnil(_rh_i)) { \
printf("___+__ "); \
} else { \
printf("%03u+%02d ", tbl##_getkey(_rh_i), tbl##_getbucket(tbl##_getkey(i))); \
} \
} \
printf("\n"); \
}
#define _ROBINHOOD_HASH_SET(tbl, key, value) { \
_ROBINHOOD_HASH_TYPICAL_INIT(key, tbl##_getbucket) \
tbl##_setvalue(0, key, value); \
int _rh_check_for_match = 1; \
for(;;) { \
if (tbl##_isnil(_rh_i)) { \
tbl##_setvalue(_rh_i, tbl##_getkey(0), tbl##_getvalue(0)); \
break; \
} else { \
if (_rh_check_for_match && (tbl##_keysequal(tbl##_getkey(_rh_i),key))) { \
tbl##_setvalue(_rh_i, key, value); \
break; \
} \
size_t _rh_i_want = tbl##_getbucket(tbl##_getkey(_rh_i)); \
size_t _rh_i_temp; \
if (_rh_i_want <= _rh_i) { \
_rh_i_temp = _rh_i - _rh_i_want; \
} else { \
_rh_i_temp = tbl##_n_elem - _rh_i_want + _rh_i - 1; \
} \
if (_rh_i_temp < _rh_temperature) { \
/* Rob the rich, give the poor */ \
tbl##_swap(0, _rh_i); \
_rh_temperature = _rh_i_temp; \
_rh_check_for_match = 0; \
} \
_ROBINHOOD_HASH_TYPICAL_INCREMENT({tbl##_overflow;}, tbl##_n_elem) \
} \
} \
}
#define _ROBINHOOD_HASH_GET(tbl, key, assignme) { \
_ROBINHOOD_HASH_TYPICAL_INIT(key, tbl##_getbucket) \
for(;;) { \
if (tbl##_isnil(_rh_i)) { \
assignme = tbl##_nilvalue; \
break; \
} else { \
if (tbl##_keysequal(tbl##_getkey(_rh_i),key)) { \
assignme = tbl##_getvalue(_rh_i); \
break; \
} \
_ROBINHOOD_HASH_TYPICAL_INCREMENT({assignme = tbl##_nilvalue;}, tbl##_n_elem) \
} \
} \
}
#define _ROBINHOOD_HASH_DEL(tbl, key) { \
_ROBINHOOD_HASH_TYPICAL_INIT(key, tbl##_getbucket) \
int _ri_needbackshift = 0; \
for(;;) { \
if (tbl##_isnil(_rh_i)) { \
tbl##_removefailed(key); \
break; \
} else { \
if (tbl##_keysequal(tbl##_getkey(_rh_i),key)) { \
tbl##_setnil(_rh_i); \
_ri_needbackshift = 1; \
break; \
} \
_ROBINHOOD_HASH_TYPICAL_INCREMENT({tbl##_removefailed(key);}, tbl##_n_elem) \
} \
} \
/* Backshift */ \
if(_ri_needbackshift)\
for(;;) { \
size_t _rh_nexti = _rh_i + 1; \
if (_rh_nexti >= tbl##_n_elem) _rh_nexti=1; \
if (tbl##_isnil(_rh_nexti)) { \
break; \
} else \
if (tbl##_getbucket(tbl##_getkey(_rh_nexti)) == _rh_nexti) { \
break; \
} else { \
tbl##_swap(_rh_nexti, _rh_i); \
} \
_rh_i = _rh_nexti; \
} \
}
#define _ROBINHOOD_HASH_SIZE(tbl, assignme) { \
size_t _rh_i; \
assignme = 0; \
for(_rh_i=0; _rh_i<tbl##_n_elem; ++_rh_i) { \
if (!(tbl##_isnil(_rh_i))) assignme+=1; \
} \
}
#define _ROBINHOOD_HASH_CLEAR(tbl) { \
size_t _rh_i; \
for(_rh_i=0; _rh_i<tbl##_n_elem; ++_rh_i) { \
tbl##_setnil(_rh_i) \
} \
}
#endif // ROBINHOOD_HASH_H