Branch data Line data Source code
1 : : /*
2 : : * The MIT License (MIT)
3 : : *
4 : : * Copyright (c) 2015 Mikkel F. Jørgensen, dvide.com
5 : : *
6 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
7 : : * of this software and associated documentation files (the "Software"), to deal
8 : : * in the Software without restriction, including without limitation the rights
9 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 : : * copies of the Software, and to permit persons to whom the Software is
11 : : * furnished to do so, subject to the following conditions:
12 : : *
13 : : * The above copyright notice and this permission notice shall be included in all
14 : : * copies or substantial portions of the Software.
15 : : *
16 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 : : * SOFTWARE.
23 : : */
24 : :
25 : :
26 : : /*
27 : : * This file implements a generic hash interface such that different
28 : : * instances have the same name, but hidden from each other.
29 : : * The interface maps the local names to a public specific type.
30 : : *
31 : : * This implementations implements a hash table with linear or quadratic
32 : : * probing.
33 : : */
34 : :
35 : : #ifdef HASH_TABLE_IMPL
36 : : #error "cannot have multiple implementations in same compilation unit"
37 : : #endif
38 : : #define HASH_TABLE_IMPL
39 : : /* Open Addressing */
40 : : #define HT_OA
41 : :
42 : : #if defined(_MSC_VER)
43 : : #pragma warning(disable: 4127) /* conditional expression is constant */
44 : : #endif
45 : :
46 : : #include <stdlib.h>
47 : : #include <memory.h>
48 : : #include <assert.h>
49 : :
50 : : #ifndef HT_PROBE
51 : : #ifdef HT_PROBE_QUADRATIC
52 : : #define HT_PROBE(k, i, N) ((k + (i + i * i) / 2) & N)
53 : : #else
54 : : #define HT_PROBE(k, i, N) ((k + i) & N)
55 : : #endif
56 : : #endif
57 : :
58 : 709 : static int ht_init(hash_table_t *ht, size_t count)
59 : : {
60 : : size_t buckets = 4;
61 : :
62 : : if ((HT_LOAD_FACTOR_FRAC) > 256 || (HT_LOAD_FACTOR_FRAC) < 1) {
63 : : /*
64 : : * 100% is bad but still the users choice.
65 : : * 101% will never terminate insertion.
66 : : */
67 : : HT_PANIC("hash table failed with impossible load factor");
68 : : return -1;
69 : : }
70 [ + + ]: 1114 : while (count > buckets * (HT_LOAD_FACTOR_FRAC) / 256) {
71 : 405 : buckets *= 2;
72 : : }
73 : 709 : ht->table = calloc(buckets, sizeof(ht_item_t));
74 [ + - ]: 709 : if (ht->table == 0) {
75 : : return -1;
76 : : }
77 : 709 : ht->offsets = 0;
78 : 709 : ht->buckets = buckets;
79 : 709 : ht->count = 0;
80 : 709 : return 0;
81 : : }
82 : :
83 : 709 : static int ht_resize(hash_table_t *ht, size_t count)
84 : : {
85 : : size_t i;
86 : : hash_table_t ht2;
87 : 709 : ht_item_t *T = ht->table;
88 : : void *item;
89 : :
90 [ - + ]: 709 : if (count < ht->count) {
91 : : count = ht->count;
92 : : }
93 [ + - ]: 709 : if (ht_init(&ht2, count)) {
94 : : return -1;
95 : : }
96 [ + + ]: 2561 : for (i = 0; i < ht->buckets; ++i) {
97 : 1852 : item = T[i];
98 [ + + ]: 1852 : if ((item && item != HT_DELETED)) {
99 : 1110 : ht_insert(&ht2, ht_key(item), ht_key_len(item), item, ht_multi);
100 : : }
101 : : }
102 : 709 : ht_clear(ht);
103 : 709 : memcpy(ht, &ht2, sizeof(*ht));
104 : 709 : return 0;
105 : : }
106 : :
107 : 2759 : static ht_item_t ht_insert(hash_table_t *ht,
108 : : const void *key, size_t len, ht_item_t new_item, int mode)
109 : : {
110 : : ht_item_t *T;
111 : : size_t N, i, j, k;
112 : : ht_item_t item, *vacant = 0;
113 : :
114 : : assert(new_item != HT_MISSING);
115 : : assert(new_item != HT_DELETED);
116 : : assert(new_item != HT_ALLOC_FAILED);
117 : :
118 [ + + ]: 2759 : if (ht->count >= ht->buckets * (HT_LOAD_FACTOR_FRAC) / 256) {
119 [ - + ]: 709 : if (ht_resize(ht, ht->count * 2)) {
120 : 0 : HT_PANIC("hash table failed to allocate memory during resize");
121 : : return HT_ALLOC_FAILED;
122 : : }
123 : : }
124 : 2759 : T = ht->table;
125 : 2759 : N = ht->buckets - 1;
126 : 225 : k = HT_HASH_FUNCTION(key, len);
127 : : i = 0;
128 : 2759 : j = HT_PROBE(k, i, N);
129 [ + + ]: 2759 : if (mode == ht_unique || mode == ht_multi) {
130 : 1110 : ++ht->count;
131 [ + + ][ - + ]: 1278 : while (T[j] && T[j] != HT_DELETED) {
132 : 168 : ++i;
133 : 168 : j = HT_PROBE(k, i, N);
134 : : }
135 : 1110 : T[j] = new_item;
136 : 1043 : return 0;
137 : : }
138 [ + + ]: 2489 : while ((item = T[j])) {
139 [ - + ]: 1099 : if (item == HT_DELETED) {
140 [ # # ]: 0 : if (vacant == 0) {
141 : : /*
142 : : * If a tombstone was found, use the first available,
143 : : * but continue search for possible match.
144 : : */
145 : : vacant = &T[j];
146 : : }
147 [ + + + ]: 1099 : } else if (ht_match(key, len, item)) {
148 [ - + ]: 259 : if (mode == ht_replace) {
149 : 0 : T[j] = new_item;
150 : : }
151 : : return item;
152 : : }
153 : 840 : ++i;
154 : 840 : j = HT_PROBE(k, i, N);
155 : : }
156 [ + - ]: 1390 : if (vacant == 0) {
157 : : vacant = &T[j];
158 : : }
159 : 1390 : ++ht->count;
160 : 1390 : *vacant = new_item;
161 : 1149 : return 0;
162 : : }
163 : :
164 : 1759 : static ht_item_t ht_find(hash_table_t *ht, const void *key, size_t len)
165 : : {
166 : 1358 : ht_item_t *T = ht->table;
167 : : size_t N, i, j, k;
168 : : ht_item_t item;
169 : :
170 [ + + ]: 1358 : if (T == 0) {
171 : : return 0;
172 : : }
173 : 1342 : N = ht->buckets - 1;
174 : 199 : k = HT_HASH_FUNCTION(key, len);
175 : : i = 0;
176 : 1342 : j = HT_PROBE(k, i, N);
177 [ + + ]: 1594 : while ((item = T[j])) {
178 [ + - ][ + + ]: 2513 : if ((item != HT_DELETED) &&
[ + + ]
179 : 167 : ht_match(key, len, item)) {
180 : : return item;
181 : : }
182 : 252 : ++i;
183 : 252 : j = HT_PROBE(k, i, N);
184 : : }
185 : : return 0;
186 : : }
187 : :
188 : 0 : static ht_item_t ht_remove(hash_table_t *ht, const void *key, size_t len)
189 : : {
190 : 0 : ht_item_t *T = ht->table;
191 : : size_t N, i, j, k;
192 : : ht_item_t item;
193 : :
194 [ # # ]: 0 : if (T == 0) {
195 : : return 0;
196 : : }
197 : 0 : N = ht->buckets - 1;
198 : 0 : k = HT_HASH_FUNCTION(key, len);
199 : : i = 0;
200 : 0 : j = HT_PROBE(k, i, N);
201 [ # # ]: 0 : while ((item = T[j])) {
202 [ # # ][ # # ]: 0 : if (item != HT_DELETED &&
[ # # ]
203 : 0 : ht_match(key, len, item)) {
204 : 0 : T[j] = HT_DELETED;
205 : 0 : --ht->count;
206 : 0 : return item;
207 : : }
208 : 0 : ++i;
209 : 0 : j = HT_PROBE(k, i, N);
210 : : }
211 : : return 0;
212 : : }
213 : :
214 : : static void ht_visit(hash_table_t *ht, ht_visitor_f *visitor, void *context)
215 : : {
216 : : size_t i;
217 : : ht_item_t *T = ht->table;
218 : : ht_item_t item;
219 : :
220 [ + + ][ + + ]: 1482 : for (i = 0; i < ht->buckets; ++i) {
221 : 1232 : item = T[i];
222 [ + + ][ + + ]: 1232 : if (item && item != HT_DELETED) {
223 : 451 : visitor(context, item);
224 : : }
225 : : }
226 : : }
227 : :
228 : 1433 : static void ht_clear(hash_table_t *ht)
229 : : {
230 [ + + ]: 1433 : if (ht->table) {
231 : 643 : free(ht->table);
232 : : }
233 : 1433 : memset(ht, 0, sizeof(*ht));
234 : 1433 : }
|