suricata
util-hashlist.c
Go to the documentation of this file.
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-hashlist.h"
31#include "util-unittest.h"
32#include "util-debug.h"
33#include "util-memcmp.h"
34
36 uint32_t (*Hash)(struct HashListTable_ *, void *, uint16_t),
37 char (*Compare)(void *, uint16_t, void *, uint16_t), void (*Free)(void *))
38{
40 HashListTable *ht = NULL;
41
42 if (size == 0) {
44 goto error;
45 }
46
47 if (Hash == NULL) {
49 goto error;
50 }
51
52 /* setup the filter */
53 ht = SCCalloc(1, sizeof(HashListTable));
54 if (unlikely(ht == NULL)) {
56 goto error;
57 }
58 ht->array_size = size;
59 ht->Hash = Hash;
60 ht->Free = Free;
61
62 if (Compare != NULL)
63 ht->Compare = Compare;
64 else
66
67 /* setup the bitarray */
68 ht->array = SCCalloc(ht->array_size, sizeof(HashListTableBucket *));
69 if (ht->array == NULL) {
71 goto error;
72 }
73
74 ht->listhead = NULL;
75 ht->listtail = NULL;
76 return ht;
77
78error:
79 if (ht != NULL) {
80 if (ht->array != NULL)
81 SCFree(ht->array);
82
83 SCFree(ht);
84 }
85 return NULL;
86}
87
89{
90 uint32_t i = 0;
91
92 if (ht == NULL)
93 return;
94
95 /* free the buckets */
96 for (i = 0; i < ht->array_size; i++) {
97 HashListTableBucket *hashbucket = ht->array[i];
98 while (hashbucket != NULL) {
99 HashListTableBucket *next_hashbucket = hashbucket->bucknext;
100 if (ht->Free != NULL)
101 ht->Free(hashbucket->data);
102 SCFree(hashbucket);
103 hashbucket = next_hashbucket;
104 }
105 }
106
107 /* free the array */
108 if (ht->array != NULL)
109 SCFree(ht->array);
110
111 SCFree(ht);
112}
113
114int HashListTableAdd(HashListTable *ht, void *data, uint16_t datalen)
115{
116 if (ht == NULL || data == NULL)
117 return -1;
118
119 uint32_t hash = ht->Hash(ht, data, datalen);
120
121 SCLogDebug("ht %p hash %"PRIu32"", ht, hash);
122
124 if (unlikely(hb == NULL))
125 goto error;
126 hb->data = data;
127 hb->size = datalen;
128 hb->bucknext = NULL;
129 hb->listnext = NULL;
130 hb->listprev = NULL;
131
132 if (ht->array[hash] == NULL) {
133 ht->array[hash] = hb;
134 } else {
135 hb->bucknext = ht->array[hash];
136 ht->array[hash] = hb;
137 }
138
139 if (ht->listtail == NULL) {
140 ht->listhead = hb;
141 ht->listtail = hb;
142 } else {
143 hb->listprev = ht->listtail;
144 ht->listtail->listnext = hb;
145 ht->listtail = hb;
146 }
147
148 return 0;
149
150error:
151 return -1;
152}
153
154int HashListTableRemove(HashListTable *ht, void *data, uint16_t datalen)
155{
156 uint32_t hash = ht->Hash(ht, data, datalen);
157
158 SCLogDebug("ht %p hash %"PRIu32"", ht, hash);
159
160 if (ht->array[hash] == NULL) {
161 SCLogDebug("ht->array[hash] NULL");
162 return -1;
163 }
164
165 /* fast track for just one data part */
166 if (ht->array[hash]->bucknext == NULL) {
167 HashListTableBucket *hb = ht->array[hash];
168
169 if (ht->Compare(hb->data,hb->size,data,datalen) == 1) {
170 /* remove from the list */
171 if (hb->listprev == NULL) {
172 ht->listhead = hb->listnext;
173 } else {
174 hb->listprev->listnext = hb->listnext;
175 }
176 if (hb->listnext == NULL) {
177 ht->listtail = hb->listprev;
178 } else {
179 hb->listnext->listprev = hb->listprev;
180 }
181
182 if (ht->Free != NULL)
183 ht->Free(hb->data);
184
185 SCFree(ht->array[hash]);
186 ht->array[hash] = NULL;
187 return 0;
188 }
189
190 SCLogDebug("fast track default case");
191 return -1;
192 }
193
194 /* more data in this bucket */
195 HashListTableBucket *hashbucket = ht->array[hash], *prev_hashbucket = NULL;
196 do {
197 if (ht->Compare(hashbucket->data,hashbucket->size,data,datalen) == 1) {
198
199 /* remove from the list */
200 if (hashbucket->listprev == NULL) {
201 ht->listhead = hashbucket->listnext;
202 } else {
203 hashbucket->listprev->listnext = hashbucket->listnext;
204 }
205 if (hashbucket->listnext == NULL) {
206 ht->listtail = hashbucket->listprev;
207 } else {
208 hashbucket->listnext->listprev = hashbucket->listprev;
209 }
210
211 if (prev_hashbucket == NULL) {
212 /* root bucket */
213 ht->array[hash] = hashbucket->bucknext;
214 } else {
215 /* child bucket */
216 prev_hashbucket->bucknext = hashbucket->bucknext;
217 }
218
219 /* remove this */
220 if (ht->Free != NULL)
221 ht->Free(hashbucket->data);
222 SCFree(hashbucket);
223 return 0;
224 }
225
226 prev_hashbucket = hashbucket;
227 hashbucket = hashbucket->bucknext;
228 } while (hashbucket != NULL);
229
230 SCLogDebug("slow track default case");
231 return -1;
232}
233
234char HashListTableDefaultCompare(void *data1, uint16_t len1, void *data2, uint16_t len2)
235{
236 if (len1 != len2)
237 return 0;
238
239 if (SCMemcmp(data1,data2,len1) != 0)
240 return 0;
241
242 return 1;
243}
244
245void *HashListTableLookup(HashListTable *ht, void *data, uint16_t datalen)
246{
247
248 if (ht == NULL) {
249 SCLogDebug("Hash List table is NULL");
250 return NULL;
251 }
252
253 uint32_t hash = ht->Hash(ht, data, datalen);
254
255 if (ht->array[hash] == NULL) {
256 return NULL;
257 }
258
259 HashListTableBucket *hashbucket = ht->array[hash];
260 do {
261 if (ht->Compare(hashbucket->data,hashbucket->size,data,datalen) == 1)
262 return hashbucket->data;
263
264 hashbucket = hashbucket->bucknext;
265 } while (hashbucket != NULL);
266
267 return NULL;
268}
269
270uint32_t HashListTableGenericHash(HashListTable *ht, void *data, uint16_t datalen)
271{
272 uint8_t *d = (uint8_t *)data;
273 uint32_t i;
274 uint32_t hash = 0;
275
276 for (i = 0; i < datalen; i++) {
277 if (i == 0) hash += (((uint32_t)*d++));
278 else if (i == 1) hash += (((uint32_t)*d++) * datalen);
279 else hash *= (((uint32_t)*d++) * i) + datalen + i;
280 }
281
282 hash *= datalen;
283 hash %= ht->array_size;
284 return hash;
285}
286
291
292/*
293 * ONLY TESTS BELOW THIS COMMENT
294 */
295
296#ifdef UNITTESTS
297static int HashListTableTestInit01 (void)
298{
300 if (ht == NULL)
301 return 0;
302
304 return 1;
305}
306
307/* no hash function, so it should fail */
308static int HashListTableTestInit02 (void)
309{
310 HashListTable *ht = HashListTableInit(1024, NULL, NULL, NULL);
311 if (ht == NULL)
312 return 1;
313
315 return 0;
316}
317
318static int HashListTableTestInit03 (void)
319{
320 int result = 0;
322 if (ht == NULL)
323 return 0;
324
326 result = 1;
327
329 return result;
330}
331
332static int HashListTableTestInit04 (void)
333{
335 if (ht == NULL)
336 return 1;
337
339 return 0;
340}
341
342static int HashListTableTestAdd01 (void)
343{
344 int result = 0;
346 if (ht == NULL)
347 goto end;
348
349 int r = HashListTableAdd(ht, (char *)"test", 0);
350 if (r != 0)
351 goto end;
352
353 /* all is good! */
354 result = 1;
355end:
356 if (ht != NULL) HashListTableFree(ht);
357 return result;
358}
359
360static int HashListTableTestAdd02 (void)
361{
362 int result = 0;
364 if (ht == NULL)
365 goto end;
366
367 int r = HashListTableAdd(ht, NULL, 4);
368 if (r == 0)
369 goto end;
370
371 /* all is good! */
372 result = 1;
373end:
374 if (ht != NULL) HashListTableFree(ht);
375 return result;
376}
377
378static int HashListTableTestAdd03 (void)
379{
380 int result = 0;
382 if (ht == NULL)
383 goto end;
384
385 int r = HashListTableAdd(ht, (char *)"test", 0);
386 if (r != 0)
387 goto end;
388
389 if (ht->listhead == NULL) {
390 printf("ht->listhead == NULL: ");
391 goto end;
392 }
393
394 if (ht->listtail == NULL) {
395 printf("ht->listtail == NULL: ");
396 goto end;
397 }
398
399 /* all is good! */
400 result = 1;
401end:
402 if (ht != NULL) HashListTableFree(ht);
403 return result;
404}
405
406static int HashListTableTestAdd04 (void)
407{
408 int result = 0;
410 if (ht == NULL)
411 goto end;
412
413 int r = HashListTableAdd(ht, (char *)"test", 4);
414 if (r != 0)
415 goto end;
416
417 char *rp = HashListTableLookup(ht, (char *)"test", 4);
418 if (rp == NULL)
419 goto end;
420
422 if (htb == NULL) {
423 printf("htb == NULL: ");
424 goto end;
425 }
426
427 char *rp2 = HashListTableGetListData(htb);
428 if (rp2 == NULL) {
429 printf("rp2 == NULL: ");
430 goto end;
431 }
432
433 if (rp != rp2) {
434 printf("rp != rp2: ");
435 goto end;
436 }
437
438 /* all is good! */
439 result = 1;
440end:
441 if (ht != NULL) HashListTableFree(ht);
442 return result;
443}
444
445static int HashListTableTestFull01 (void)
446{
447 int result = 0;
449 if (ht == NULL)
450 goto end;
451
452 int r = HashListTableAdd(ht, (char *)"test", 4);
453 if (r != 0)
454 goto end;
455
456 char *rp = HashListTableLookup(ht, (char *)"test", 4);
457 if (rp == NULL)
458 goto end;
459
460 r = HashListTableRemove(ht, (char *)"test", 4);
461 if (r != 0)
462 goto end;
463
464 /* all is good! */
465 result = 1;
466end:
467 if (ht != NULL) HashListTableFree(ht);
468 return result;
469}
470
471static int HashListTableTestFull02 (void)
472{
473 int result = 0;
475 if (ht == NULL)
476 goto end;
477
478 int r = HashListTableAdd(ht, (char *)"test", 4);
479 if (r != 0)
480 goto end;
481
482 char *rp = HashListTableLookup(ht, (char *)"test", 4);
483 if (rp == NULL)
484 goto end;
485
486 r = HashListTableRemove(ht, (char *)"test2", 5);
487 if (r == 0)
488 goto end;
489
490 /* all is good! */
491 result = 1;
492end:
493 if (ht != NULL) HashListTableFree(ht);
494 return result;
495}
496#endif /* UNITTESTS */
497
499{
500#ifdef UNITTESTS
501 UtRegisterTest("HashListTableTestInit01", HashListTableTestInit01);
502 UtRegisterTest("HashListTableTestInit02", HashListTableTestInit02);
503 UtRegisterTest("HashListTableTestInit03", HashListTableTestInit03);
504 UtRegisterTest("HashListTableTestInit04", HashListTableTestInit04);
505
506 UtRegisterTest("HashListTableTestAdd01", HashListTableTestAdd01);
507 UtRegisterTest("HashListTableTestAdd02", HashListTableTestAdd02);
508 UtRegisterTest("HashListTableTestAdd03", HashListTableTestAdd03);
509 UtRegisterTest("HashListTableTestAdd04", HashListTableTestAdd04);
510
511 UtRegisterTest("HashListTableTestFull01", HashListTableTestFull01);
512 UtRegisterTest("HashListTableTestFull02", HashListTableTestFull02);
513#endif /* UNITTESTS */
514}
515
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
struct HashListTableBucket_ * listnext
struct HashListTableBucket_ * listprev
struct HashListTableBucket_ * bucknext
HashListTableBucket * listhead
HashListTableBucket * listtail
HashListTableBucket ** array
void(* Free)(void *)
uint32_t(* Hash)(struct HashListTable_ *, void *, uint16_t)
char(* Compare)(void *, uint16_t, void *, uint16_t)
uint32_t array_size
#define SCLogDebug(...)
Definition util-debug.h:275
thread_local SCError sc_errno
Definition util-error.c:31
@ SC_EINVAL
Definition util-error.h:30
@ SC_ENOMEM
Definition util-error.h:29
@ SC_OK
Definition util-error.h:27
char HashListTableDefaultCompare(void *data1, uint16_t len1, void *data2, uint16_t len2)
void * HashListTableLookup(HashListTable *ht, void *data, uint16_t datalen)
int HashListTableAdd(HashListTable *ht, void *data, uint16_t datalen)
HashListTableBucket * HashListTableGetListHead(HashListTable *ht)
void HashListTableRegisterTests(void)
uint32_t HashListTableGenericHash(HashListTable *ht, void *data, uint16_t datalen)
int HashListTableRemove(HashListTable *ht, void *data, uint16_t datalen)
HashListTable * HashListTableInit(uint32_t size, uint32_t(*Hash)(struct HashListTable_ *, void *, uint16_t), char(*Compare)(void *, uint16_t, void *, uint16_t), void(*Free)(void *))
void HashListTableFree(HashListTable *ht)
#define HashListTableGetListData(hb)
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define SCMemcmp(a, b, c)
#define unlikely(expr)