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 {
39  sc_errno = SC_OK;
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 
78 error:
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 
114 int 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 
150 error:
151  return -1;
152 }
153 
154 int 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 
234 char 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 
245 void *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 
270 uint32_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 
288 {
289  return ht->listhead;
290 }
291 
292 /*
293  * ONLY TESTS BELOW THIS COMMENT
294  */
295 
296 #ifdef UNITTESTS
297 static int HashListTableTestInit01 (void)
298 {
300  if (ht == NULL)
301  return 0;
302 
303  HashListTableFree(ht);
304  return 1;
305 }
306 
307 /* no hash function, so it should fail */
308 static int HashListTableTestInit02 (void)
309 {
310  HashListTable *ht = HashListTableInit(1024, NULL, NULL, NULL);
311  if (ht == NULL)
312  return 1;
313 
314  HashListTableFree(ht);
315  return 0;
316 }
317 
318 static int HashListTableTestInit03 (void)
319 {
320  int result = 0;
322  if (ht == NULL)
323  return 0;
324 
325  if (ht->Hash == HashListTableGenericHash)
326  result = 1;
327 
328  HashListTableFree(ht);
329  return result;
330 }
331 
332 static int HashListTableTestInit04 (void)
333 {
335  if (ht == NULL)
336  return 1;
337 
338  HashListTableFree(ht);
339  return 0;
340 }
341 
342 static 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;
355 end:
356  if (ht != NULL) HashListTableFree(ht);
357  return result;
358 }
359 
360 static 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;
373 end:
374  if (ht != NULL) HashListTableFree(ht);
375  return result;
376 }
377 
378 static 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;
401 end:
402  if (ht != NULL) HashListTableFree(ht);
403  return result;
404 }
405 
406 static 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;
440 end:
441  if (ht != NULL) HashListTableFree(ht);
442  return result;
443 }
444 
445 static 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;
466 end:
467  if (ht != NULL) HashListTableFree(ht);
468  return result;
469 }
470 
471 static 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;
492 end:
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 
HashListTableGetListData
#define HashListTableGetListData(hb)
Definition: util-hashlist.h:56
util-hashlist.h
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
SC_EINVAL
@ SC_EINVAL
Definition: util-error.h:30
HashListTable_::Free
void(* Free)(void *)
Definition: util-hashlist.h:44
HashListTableGetListHead
HashListTableBucket * HashListTableGetListHead(HashListTable *ht)
Definition: util-hashlist.c:287
HashListTableBucket_::size
uint16_t size
Definition: util-hashlist.h:30
HashListTableLookup
void * HashListTableLookup(HashListTable *ht, void *data, uint16_t datalen)
Definition: util-hashlist.c:245
HashListTableBucket_::listprev
struct HashListTableBucket_ * listprev
Definition: util-hashlist.h:33
util-unittest.h
SC_ENOMEM
@ SC_ENOMEM
Definition: util-error.h:29
HashListTableAdd
int HashListTableAdd(HashListTable *ht, void *data, uint16_t datalen)
Definition: util-hashlist.c:114
HashListTable_::array_size
uint32_t array_size
Definition: util-hashlist.h:41
util-memcmp.h
HashListTableInit
HashListTable * HashListTableInit(uint32_t size, uint32_t(*Hash)(struct HashListTable_ *, void *, uint16_t), char(*Compare)(void *, uint16_t, void *, uint16_t), void(*Free)(void *))
Definition: util-hashlist.c:35
util-debug.h
HashTable_::Compare
char(* Compare)(void *, uint16_t, void *, uint16_t)
Definition: util-hash.h:42
HashListTableDefaultCompare
char HashListTableDefaultCompare(void *data1, uint16_t len1, void *data2, uint16_t len2)
Definition: util-hashlist.c:234
HashListTableGenericHash
uint32_t HashListTableGenericHash(HashListTable *ht, void *data, uint16_t datalen)
Definition: util-hashlist.c:270
HashListTable_::Compare
char(* Compare)(void *, uint16_t, void *, uint16_t)
Definition: util-hashlist.h:43
HashListTable_::Hash
uint32_t(* Hash)(struct HashListTable_ *, void *, uint16_t)
Definition: util-hashlist.h:42
SC_OK
@ SC_OK
Definition: util-error.h:27
HashListTable_::listtail
HashListTableBucket * listtail
Definition: util-hashlist.h:40
HashListTable_
Definition: util-hashlist.h:37
HashListTable_::listhead
HashListTableBucket * listhead
Definition: util-hashlist.h:39
HashListTable_::array
HashListTableBucket ** array
Definition: util-hashlist.h:38
suricata-common.h
HashListTableFree
void HashListTableFree(HashListTable *ht)
Definition: util-hashlist.c:88
HashListTableBucket_::listnext
struct HashListTableBucket_ * listnext
Definition: util-hashlist.h:32
SCFree
#define SCFree(p)
Definition: util-mem.h:61
HashListTableRegisterTests
void HashListTableRegisterTests(void)
Definition: util-hashlist.c:498
HashListTableBucket_::data
void * data
Definition: util-hashlist.h:29
HashListTableBucket_
Definition: util-hashlist.h:28
HashListTableRemove
int HashListTableRemove(HashListTable *ht, void *data, uint16_t datalen)
Definition: util-hashlist.c:154
sc_errno
thread_local SCError sc_errno
Definition: util-error.c:31
HashListTableBucket_::bucknext
struct HashListTableBucket_ * bucknext
Definition: util-hashlist.h:31
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCMemcmp
#define SCMemcmp(a, b, c)
Definition: util-memcmp.h:290
HashTable_::Free
void(* Free)(void *)
Definition: util-hash.h:43
HashTable_::Hash
uint32_t(* Hash)(struct HashTable_ *, void *, uint16_t)
Definition: util-hash.h:41