/src/suricata7/src/util-hash.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2010 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** |
19 | | * \file |
20 | | * |
21 | | * \author Victor Julien <victor@inliniac.net> |
22 | | * |
23 | | * Chained hash table implementation |
24 | | * |
25 | | * The 'Free' pointer can be used to have the API free your |
26 | | * hashed data. If it's NULL it's the callers responsibility |
27 | | */ |
28 | | |
29 | | #include "suricata-common.h" |
30 | | #include "util-hash.h" |
31 | | #include "util-unittest.h" |
32 | | #include "util-memcmp.h" |
33 | | #include "util-debug.h" |
34 | | |
35 | 452k | HashTable* HashTableInit(uint32_t size, uint32_t (*Hash)(struct HashTable_ *, void *, uint16_t), char (*Compare)(void *, uint16_t, void *, uint16_t), void (*Free)(void *)) { |
36 | | |
37 | 452k | HashTable *ht = NULL; |
38 | | |
39 | 452k | if (size == 0) { |
40 | 0 | goto error; |
41 | 0 | } |
42 | | |
43 | 452k | if (Hash == NULL) { |
44 | | //printf("ERROR: HashTableInit no Hash function\n"); |
45 | 0 | goto error; |
46 | 0 | } |
47 | | |
48 | | /* setup the filter */ |
49 | 452k | ht = SCMalloc(sizeof(HashTable)); |
50 | 452k | if (unlikely(ht == NULL)) |
51 | 0 | goto error; |
52 | 452k | memset(ht,0,sizeof(HashTable)); |
53 | 452k | ht->array_size = size; |
54 | 452k | ht->Hash = Hash; |
55 | 452k | ht->Free = Free; |
56 | | |
57 | 452k | if (Compare != NULL) |
58 | 452k | ht->Compare = Compare; |
59 | 0 | else |
60 | 0 | ht->Compare = HashTableDefaultCompare; |
61 | | |
62 | | /* setup the bitarray */ |
63 | 452k | ht->array = SCMalloc(ht->array_size * sizeof(HashTableBucket *)); |
64 | 452k | if (ht->array == NULL) |
65 | 0 | goto error; |
66 | 452k | memset(ht->array,0,ht->array_size * sizeof(HashTableBucket *)); |
67 | | |
68 | 452k | return ht; |
69 | | |
70 | 0 | error: |
71 | 0 | if (ht != NULL) { |
72 | 0 | if (ht->array != NULL) |
73 | 0 | SCFree(ht->array); |
74 | |
|
75 | 0 | SCFree(ht); |
76 | 0 | } |
77 | 0 | return NULL; |
78 | 452k | } |
79 | | |
80 | | void HashTableFree(HashTable *ht) |
81 | 202k | { |
82 | 202k | uint32_t i = 0; |
83 | | |
84 | 202k | if (ht == NULL) |
85 | 0 | return; |
86 | | |
87 | | /* free the buckets */ |
88 | 294M | for (i = 0; i < ht->array_size; i++) { |
89 | 293M | HashTableBucket *hashbucket = ht->array[i]; |
90 | 294M | while (hashbucket != NULL) { |
91 | 47.7k | HashTableBucket *next_hashbucket = hashbucket->next; |
92 | 47.7k | if (ht->Free != NULL) |
93 | 47.7k | ht->Free(hashbucket->data); |
94 | 47.7k | SCFree(hashbucket); |
95 | 47.7k | hashbucket = next_hashbucket; |
96 | 47.7k | } |
97 | 293M | } |
98 | | |
99 | | /* free the array */ |
100 | 202k | if (ht->array != NULL) |
101 | 202k | SCFree(ht->array); |
102 | | |
103 | 202k | SCFree(ht); |
104 | 202k | } |
105 | | |
106 | | void HashTablePrint(HashTable *ht) |
107 | 0 | { |
108 | 0 | printf("\n----------- Hash Table Stats ------------\n"); |
109 | 0 | printf("Buckets: %" PRIu32 "\n", ht->array_size); |
110 | 0 | printf("Hash function pointer: %p\n", ht->Hash); |
111 | 0 | printf("-----------------------------------------\n"); |
112 | 0 | } |
113 | | |
114 | | int HashTableAdd(HashTable *ht, void *data, uint16_t datalen) |
115 | 238k | { |
116 | 238k | if (ht == NULL || data == NULL) |
117 | 0 | return -1; |
118 | | |
119 | 238k | uint32_t hash = ht->Hash(ht, data, datalen); |
120 | | |
121 | 238k | HashTableBucket *hb = SCMalloc(sizeof(HashTableBucket)); |
122 | 238k | if (unlikely(hb == NULL)) |
123 | 0 | goto error; |
124 | 238k | memset(hb, 0, sizeof(HashTableBucket)); |
125 | 238k | hb->data = data; |
126 | 238k | hb->size = datalen; |
127 | 238k | hb->next = NULL; |
128 | | |
129 | 238k | if (hash >= ht->array_size) { |
130 | 0 | SCLogWarning("attempt to insert element out of hash array\n"); |
131 | 0 | goto error; |
132 | 0 | } |
133 | | |
134 | 238k | if (ht->array[hash] == NULL) { |
135 | 118k | ht->array[hash] = hb; |
136 | 119k | } else { |
137 | 119k | hb->next = ht->array[hash]; |
138 | 119k | ht->array[hash] = hb; |
139 | 119k | } |
140 | | |
141 | | #ifdef UNITTESTS |
142 | | ht->count++; |
143 | | #endif |
144 | | |
145 | 238k | return 0; |
146 | | |
147 | 0 | error: |
148 | 0 | if (hb != NULL) |
149 | 0 | SCFree(hb); |
150 | 0 | return -1; |
151 | 238k | } |
152 | | |
153 | | int HashTableRemove(HashTable *ht, void *data, uint16_t datalen) |
154 | 0 | { |
155 | 0 | uint32_t hash = ht->Hash(ht, data, datalen); |
156 | |
|
157 | 0 | if (ht->array[hash] == NULL) { |
158 | 0 | return -1; |
159 | 0 | } |
160 | | |
161 | 0 | if (ht->array[hash]->next == NULL) { |
162 | 0 | if (ht->Free != NULL) |
163 | 0 | ht->Free(ht->array[hash]->data); |
164 | 0 | SCFree(ht->array[hash]); |
165 | 0 | ht->array[hash] = NULL; |
166 | 0 | return 0; |
167 | 0 | } |
168 | | |
169 | 0 | HashTableBucket *hashbucket = ht->array[hash], *prev_hashbucket = NULL; |
170 | 0 | do { |
171 | 0 | if (ht->Compare(hashbucket->data,hashbucket->size,data,datalen) == 1) { |
172 | 0 | if (prev_hashbucket == NULL) { |
173 | | /* root bucket */ |
174 | 0 | ht->array[hash] = hashbucket->next; |
175 | 0 | } else { |
176 | | /* child bucket */ |
177 | 0 | prev_hashbucket->next = hashbucket->next; |
178 | 0 | } |
179 | | |
180 | | /* remove this */ |
181 | 0 | if (ht->Free != NULL) |
182 | 0 | ht->Free(hashbucket->data); |
183 | 0 | SCFree(hashbucket); |
184 | 0 | return 0; |
185 | 0 | } |
186 | | |
187 | 0 | prev_hashbucket = hashbucket; |
188 | 0 | hashbucket = hashbucket->next; |
189 | 0 | } while (hashbucket != NULL); |
190 | | |
191 | 0 | return -1; |
192 | 0 | } |
193 | | |
194 | | void *HashTableLookup(HashTable *ht, void *data, uint16_t datalen) |
195 | 1.11M | { |
196 | 1.11M | uint32_t hash = 0; |
197 | | |
198 | 1.11M | if (ht == NULL) |
199 | 2 | return NULL; |
200 | | |
201 | 1.11M | hash = ht->Hash(ht, data, datalen); |
202 | | |
203 | 1.11M | if (hash >= ht->array_size) { |
204 | 0 | SCLogWarning("attempt to access element out of hash array\n"); |
205 | 0 | return NULL; |
206 | 0 | } |
207 | | |
208 | 1.11M | if (ht->array[hash] == NULL) |
209 | 101k | return NULL; |
210 | | |
211 | 1.00M | HashTableBucket *hashbucket = ht->array[hash]; |
212 | 1.12M | do { |
213 | 1.12M | if (ht->Compare(hashbucket->data, hashbucket->size, data, datalen) == 1) |
214 | 1.00M | return hashbucket->data; |
215 | | |
216 | 115k | hashbucket = hashbucket->next; |
217 | 115k | } while (hashbucket != NULL); |
218 | | |
219 | 4.62k | return NULL; |
220 | 1.00M | } |
221 | | |
222 | | uint32_t HashTableGenericHash(HashTable *ht, void *data, uint16_t datalen) |
223 | 0 | { |
224 | 0 | uint8_t *d = (uint8_t *)data; |
225 | 0 | uint32_t i; |
226 | 0 | uint32_t hash = 0; |
227 | |
|
228 | 0 | for (i = 0; i < datalen; i++) { |
229 | 0 | if (i == 0) hash += (((uint32_t)*d++)); |
230 | 0 | else if (i == 1) hash += (((uint32_t)*d++) * datalen); |
231 | 0 | else hash *= (((uint32_t)*d++) * i) + datalen + i; |
232 | 0 | } |
233 | |
|
234 | 0 | hash *= datalen; |
235 | 0 | hash %= ht->array_size; |
236 | 0 | return hash; |
237 | 0 | } |
238 | | |
239 | | char HashTableDefaultCompare(void *data1, uint16_t len1, void *data2, uint16_t len2) |
240 | 0 | { |
241 | 0 | if (len1 != len2) |
242 | 0 | return 0; |
243 | | |
244 | 0 | if (SCMemcmp(data1,data2,len1) != 0) |
245 | 0 | return 0; |
246 | | |
247 | 0 | return 1; |
248 | 0 | } |
249 | | |
250 | | /* |
251 | | * ONLY TESTS BELOW THIS COMMENT |
252 | | */ |
253 | | |
254 | | #ifdef UNITTESTS |
255 | | static int HashTableTestInit01 (void) |
256 | | { |
257 | | HashTable *ht = HashTableInit(1024, HashTableGenericHash, NULL, NULL); |
258 | | if (ht == NULL) |
259 | | return 0; |
260 | | |
261 | | HashTableFree(ht); |
262 | | return 1; |
263 | | } |
264 | | |
265 | | /* no hash function, so it should fail */ |
266 | | static int HashTableTestInit02 (void) |
267 | | { |
268 | | HashTable *ht = HashTableInit(1024, NULL, NULL, NULL); |
269 | | if (ht == NULL) |
270 | | return 1; |
271 | | |
272 | | HashTableFree(ht); |
273 | | return 0; |
274 | | } |
275 | | |
276 | | static int HashTableTestInit03 (void) |
277 | | { |
278 | | int result = 0; |
279 | | HashTable *ht = HashTableInit(1024, HashTableGenericHash, NULL, NULL); |
280 | | if (ht == NULL) |
281 | | return 0; |
282 | | |
283 | | if (ht->Hash == HashTableGenericHash) |
284 | | result = 1; |
285 | | |
286 | | HashTableFree(ht); |
287 | | return result; |
288 | | } |
289 | | |
290 | | static int HashTableTestInit04 (void) |
291 | | { |
292 | | HashTable *ht = HashTableInit(0, HashTableGenericHash, NULL, NULL); |
293 | | if (ht == NULL) |
294 | | return 1; |
295 | | |
296 | | HashTableFree(ht); |
297 | | return 0; |
298 | | } |
299 | | |
300 | | static int HashTableTestInit05 (void) |
301 | | { |
302 | | int result = 0; |
303 | | HashTable *ht = HashTableInit(1024, HashTableGenericHash, NULL, NULL); |
304 | | if (ht == NULL) |
305 | | return 0; |
306 | | |
307 | | if (ht->Compare == HashTableDefaultCompare) |
308 | | result = 1; |
309 | | |
310 | | HashTableFree(ht); |
311 | | return result; |
312 | | } |
313 | | |
314 | | static char HashTableDefaultCompareTest(void *data1, uint16_t len1, void *data2, uint16_t len2) |
315 | | { |
316 | | if (len1 != len2) |
317 | | return 0; |
318 | | |
319 | | if (SCMemcmp(data1,data2,len1) != 0) |
320 | | return 0; |
321 | | |
322 | | return 1; |
323 | | } |
324 | | |
325 | | static int HashTableTestInit06 (void) |
326 | | { |
327 | | int result = 0; |
328 | | HashTable *ht = HashTableInit(1024, HashTableGenericHash, HashTableDefaultCompareTest, NULL); |
329 | | if (ht == NULL) |
330 | | return 0; |
331 | | |
332 | | if (ht->Compare == HashTableDefaultCompareTest) |
333 | | result = 1; |
334 | | |
335 | | HashTableFree(ht); |
336 | | return result; |
337 | | } |
338 | | |
339 | | static int HashTableTestAdd01 (void) |
340 | | { |
341 | | int result = 0; |
342 | | HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL); |
343 | | if (ht == NULL) |
344 | | goto end; |
345 | | |
346 | | int r = HashTableAdd(ht, (char *)"test", 0); |
347 | | if (r != 0) |
348 | | goto end; |
349 | | |
350 | | /* all is good! */ |
351 | | result = 1; |
352 | | end: |
353 | | if (ht != NULL) HashTableFree(ht); |
354 | | return result; |
355 | | } |
356 | | |
357 | | static int HashTableTestAdd02 (void) |
358 | | { |
359 | | int result = 0; |
360 | | HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL); |
361 | | if (ht == NULL) |
362 | | goto end; |
363 | | |
364 | | int r = HashTableAdd(ht, NULL, 4); |
365 | | if (r == 0) |
366 | | goto end; |
367 | | |
368 | | /* all is good! */ |
369 | | result = 1; |
370 | | end: |
371 | | if (ht != NULL) HashTableFree(ht); |
372 | | return result; |
373 | | } |
374 | | |
375 | | static int HashTableTestFull01 (void) |
376 | | { |
377 | | int result = 0; |
378 | | HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL); |
379 | | if (ht == NULL) |
380 | | goto end; |
381 | | |
382 | | int r = HashTableAdd(ht, (char *)"test", 4); |
383 | | if (r != 0) |
384 | | goto end; |
385 | | |
386 | | char *rp = HashTableLookup(ht, (char *)"test", 4); |
387 | | if (rp == NULL) |
388 | | goto end; |
389 | | |
390 | | r = HashTableRemove(ht, (char *)"test", 4); |
391 | | if (r != 0) |
392 | | goto end; |
393 | | |
394 | | /* all is good! */ |
395 | | result = 1; |
396 | | end: |
397 | | if (ht != NULL) HashTableFree(ht); |
398 | | return result; |
399 | | } |
400 | | |
401 | | static int HashTableTestFull02 (void) |
402 | | { |
403 | | int result = 0; |
404 | | HashTable *ht = HashTableInit(32, HashTableGenericHash, NULL, NULL); |
405 | | if (ht == NULL) |
406 | | goto end; |
407 | | |
408 | | int r = HashTableAdd(ht, (char *)"test", 4); |
409 | | if (r != 0) |
410 | | goto end; |
411 | | |
412 | | char *rp = HashTableLookup(ht, (char *)"test", 4); |
413 | | if (rp == NULL) |
414 | | goto end; |
415 | | |
416 | | r = HashTableRemove(ht, (char *)"test2", 5); |
417 | | if (r == 0) |
418 | | goto end; |
419 | | |
420 | | /* all is good! */ |
421 | | result = 1; |
422 | | end: |
423 | | if (ht != NULL) HashTableFree(ht); |
424 | | return result; |
425 | | } |
426 | | #endif |
427 | | |
428 | | void HashTableRegisterTests(void) |
429 | 0 | { |
430 | | #ifdef UNITTESTS |
431 | | UtRegisterTest("HashTableTestInit01", HashTableTestInit01); |
432 | | UtRegisterTest("HashTableTestInit02", HashTableTestInit02); |
433 | | UtRegisterTest("HashTableTestInit03", HashTableTestInit03); |
434 | | UtRegisterTest("HashTableTestInit04", HashTableTestInit04); |
435 | | UtRegisterTest("HashTableTestInit05", HashTableTestInit05); |
436 | | UtRegisterTest("HashTableTestInit06", HashTableTestInit06); |
437 | | |
438 | | UtRegisterTest("HashTableTestAdd01", HashTableTestAdd01); |
439 | | UtRegisterTest("HashTableTestAdd02", HashTableTestAdd02); |
440 | | |
441 | | UtRegisterTest("HashTableTestFull01", HashTableTestFull01); |
442 | | UtRegisterTest("HashTableTestFull02", HashTableTestFull02); |
443 | | #endif |
444 | 0 | } |
445 | | |